blob: e0ec69cdcc05b88c262879f8434c5ea6360d0872 [file] [log] [blame]
Basil Eljuse4b14afb2020-09-30 13:07:23 +01001#!/usr/bin/env bash
2
3##############################################################################
4# Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
5#
6# SPDX-License-Identifier: GPL-2.0-only
7##############################################################################
8
9#==============================================================================
10# FILE: merge.sh
11#
12# DESCRIPTION: Wrapper to merge intermediate json files and LCOV trace .info
13# files.
14#==============================================================================
15
saul-romero-arm6c40b242021-06-18 10:21:04 +000016set +x
Basil Eljuse4b14afb2020-09-30 13:07:23 +010017#################################################################
18# Function to manipulate json objects.
19# The json object properties can be accessed through "." separated
20# property names. There are special characters that define a function
21# over a given property value:
22# If the qualifier list starts with '-' then is asking for the len of the
23# json array defined by the qualifiers.
24# If the qualifier list starts with '*' then the resulting json value
25# is returned without double quotes at the end and the beginning.
26# If some property name starts with "?" then is requesting if that
27# property exists within the json object.
28# Globals:
29# None
30# Arguments:
31# 1-Json string that describes the json object
32# 2-String of '.' separated qualifiers to access properties
33# within the json object
34# 3- Optional default value for a sought property value
35# Outputs:
36# None
37################################################################
38get_json_object() {
39 export _json_string="$1"
40 export _qualifiers="$2"
41 export _default="$3"
42 python3 - << EOT
43import os
44import json
45import sys
46
47_json_string = os.getenv("_json_string", "")
48_qualifiers = os.getenv("_qualifiers", "")
49_default = os.getenv("_default", "")
50try:
51 data = json.loads(_json_string)
52except Exception as ex:
53 print("Error decoding json string:{}".format(ex))
54 sys.exit(-1)
55ptr = data
56if _qualifiers[0] in ['-', '*']:
57 cmd = _qualifiers[0]
58 _qualifiers = _qualifiers[1:]
59else:
60 cmd = ""
61for _name in _qualifiers.split("."):
62 if _name in ptr:
63 ptr = ptr[_name]
64 elif _name.isdigit() and int(_name) < len(ptr):
65 ptr = ptr[int(_name)]
66 elif _name.startswith("?"):
67 print(_name[1:] in ptr)
68 sys.exit(0)
69 elif _default:
70 print(_default)
71 sys.exit(0)
72 else:
73 print("'{}' is not in the json object".format(_name))
74 sys.exit(-1)
75if cmd == "-":
76 # return len of the json array
77 print(len(ptr))
78elif cmd == "*":
79 #remove quotes
80 string = json.dumps(ptr)
81 if string.startswith('"') and string.endswith('"'):
82 string = string[1:-1]
83 print(string)
84else:
85 print(json.dumps(ptr))
86EOT
87}
88
89#################################################################
90# Convert a relative path to absolute path
91# Globals:
92# None
93# Arguments:
94# 1-Path to be converted
95# Outputs:
96# Absolute path
97################################################################
98get_abs_path() {
99 path="$1"
100 echo "$(cd $(dirname $path) && echo "$(pwd -P)"/$(basename $path))"
101}
102
103#################################################################
104# Clone the source files
105# Globals:
106# None
107# Arguments:
108# 1-Json file with the sources to be cloned
109# 2-Folder where to clone the sources
110# Outputs:
111# None
112################################################################
113clone_repos() {
114 export OUTPUT_JSON="$1"
115 export CSOURCE_FOLDER="${2:-$LOCAL_WORKSPACE}"
116
117 cd $DIR # To be run at the same level of this script
118python3 - << EOT
119import os
120import clone_sources
121
122output_file = os.getenv('OUTPUT_JSON', 'output_file.json')
123source_folder = os.getenv('CSOURCE_FOLDER', 'source')
124try:
125 r = clone_sources.CloneSources(output_file)
126 r.clone_repo(source_folder)
127except Exception as ex:
128 print(ex)
129EOT
saul-romero-arm6c40b242021-06-18 10:21:04 +0000130 cd -
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100131}
132
133#################################################################
134# Get the a file defined in the json object
135# Globals:
136# None
137# Arguments:
138# 1-Json object that defines the locations of the info and json
139# files
140# 2-Folder to save the info and json files
Saul Romero884d2142023-01-16 10:31:22 +0000141# 3-Reference argument to hold the copied file name location
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100142# Outputs:
143# None
144################################################################
145get_file() {
146 json_object="$1"
147 where="$2"
148 var_name="${3:-param_cloned}" # Defaults to globar var
149
150 local _type=$(get_json_object "$json_object" "type")
151 local _origin=$(get_json_object "$json_object" "*origin")
152 local _compression=$(get_json_object "$json_object" "*compression" None)
153 local fname=""
154 local cloned_file=""
155 local full_filename=$(basename -- "$_origin")
156 local extension="${full_filename##*.}"
157 local filename="${full_filename%.*}"
158
159 if [ "$_type" = '"http"' ];then
160 fname="$where.$extension" # Same filename as folder
161 rm $where/$fname &>/dev/null || true
162 wget -o error.log $_origin -O $where/$fname || (
saul-romero-arm6c40b242021-06-18 10:21:04 +0000163 cat error.log && exit -1)
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100164 cloned_file="$(get_abs_path $where/$fname)"
165 elif [ "$_type" = '"bundle"' ];then
166 # Check file exists at origin, i.e. was unbundled before
167 fname="$_origin"
168 if [ -f "$where/$fname" ];then
169 cloned_file="$(get_abs_path $where/$fname)"
170 fi
171 elif [ "$_type" = '"file"' ];then
saul-romero-arm6c40b242021-06-18 10:21:04 +0000172 if [[ "$_origin" = http* ]]; then
173 echo "$_origin looks like 'http' rather than 'file' please check..."
174 exit -1
175 fi
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100176 fname="$where.$extension" # Same filename as folder
177 cp -f $_origin $where/$fname
178 cloned_file="$(get_abs_path $where/$fname)"
179 else
180 echo "Error unsupported file type:$_type.... Aborting."
181 exit -1
182 fi
183 if [ "$_compression" = "tar.xz" ];then
184 cd $where
185 pwd
186 tar -xzf $fname
187 rm -f $fname
188 cd -
189 fi
190 eval "${var_name}=${cloned_file}"
191}
192
193#####################################################################
Saul Romero884d2142023-01-16 10:31:22 +0000194# Get (download/copy) info and json files from the configuration json
195# file
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100196# Globals:
Saul Romero884d2142023-01-16 10:31:22 +0000197# merge_configuration_file: Input json file with locations of info
198# and json scm files to be merged.
199# info_files: Array of info file locations.
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100200# Arguments:
Saul Romero884d2142023-01-16 10:31:22 +0000201# 1: Target folder to download info and json files to be merged.
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100202# Outputs:
203# None
204###################################################################
205get_info_json_files() {
Saul Romero884d2142023-01-16 10:31:22 +0000206 local input_folder="${1:-$LCOV_FOLDER}"
207 local json_string="$(cat $merge_configuration_file)"
208 local config_json_file=""
209 local info_file=""
210 # Get files array
211 local nf=$(get_json_object "$json_string" "-files")
212 # Init target folder
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100213 rm -rf $input_folder > /dev/null || true
214 mkdir -p $input_folder
Saul Romero884d2142023-01-16 10:31:22 +0000215 # Iterate through each file element and get the files
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100216 for f in $(seq 0 $(($nf - 1)));
217 do
218 pushd $input_folder > /dev/null
219 _file=$(get_json_object "$json_string" "files.$f")
Saul Romero884d2142023-01-16 10:31:22 +0000220 # The name of the folder is the 'id' value
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100221 folder=$(get_json_object "$_file" "*id")
Saul Romero884d2142023-01-16 10:31:22 +0000222 echo "Getting files from project '$folder' into '$input_folder'..."
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100223 mkdir -p $folder
224 bundles=$(get_json_object "$_file" "bundles" None)
225 if [ "$bundles" != "None" ];then
226 nb=$(get_json_object "$_file" "-bundles")
227 for n in $(seq 0 $(($nb - 1)));
228 do
229 get_file "$(get_json_object "$bundles" "$n")" $folder
230 done
231 fi
Saul Romero884d2142023-01-16 10:31:22 +0000232 # Download/copy files and save their locations
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100233 get_file "$(get_json_object "$_file" "config")" $folder config_json_file
234 get_file "$(get_json_object "$_file" "info")" $folder info_file
Saul Romero884d2142023-01-16 10:31:22 +0000235 info_files+=($info_file)
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100236 popd > /dev/null
237 done
238}
239
240#################################################################
241# Merge json and info files and generate branch coverage report
242# Globals:
Saul Romero884d2142023-01-16 10:31:22 +0000243# merged_coverage_file: Location and name for merged coverage info
244# merged_json_file: Location and name for merged json scm sources
245# LOCAL_WORKSPACE: Local workspace folder with the source code files
246# generate_local: Flag to generate local lcov reports
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100247# Arguments:
Saul Romero884d2142023-01-16 10:31:22 +0000248# 1: Location where reside json and info files
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100249# Outputs:
Saul Romero884d2142023-01-16 10:31:22 +0000250# Merged coverage file
251# Merged json file
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100252################################################################
253merge_files() {
Saul Romero884d2142023-01-16 10:31:22 +0000254 local input_folder="${1:-$LCOV_FOLDER}"
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100255# Merge info and json files
256 local lc=" "
257 if [ -n "$LOCAL_WORKSPACE" ];then
Saul Romero884d2142023-01-16 10:31:22 +0000258 # Translation from info workspaces into local workspace
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100259 lc=" --local-workspace $LOCAL_WORKSPACE"
260 fi
Saul Romero884d2142023-01-16 10:31:22 +0000261 if [ "$generate_local" = true ];then
262 # Generate local reports
263 lc="${lc} -k"
264 fi
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100265 # Getting the path of the merge.py must reside at the same
266 # path as the merge.sh
267 python3 ${DIR}/merge.py \
268 $(find $input_folder -name "*.info" -exec echo "-a {}" \;) \
269 $(find $input_folder -name "*.json" -exec echo "-j {}" \;) \
Saul Romero884d2142023-01-16 10:31:22 +0000270 -o $merged_coverage_file \
271 -m $merged_json_file \
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100272 $lc
273
274}
275
276
277#################################################################
Saul Romero884d2142023-01-16 10:31:22 +0000278# Generate local lcov reports
279# Globals:
280# info_files: Array of locations and names of info files
281# Arguments:
282# None
283# Outputs:
284# Lcov report files for each info file
285################################################################
286generate_local_reports() {
287 for i in ${!info_files[@]};
288 do
289 local info_file=${info_files[$i]}
290 local parentdir=$(dirname "$info_file")
291 local t_info_file="${info_file/.info/_local.info}"
292 genhtml --branch-coverage $t_info_file \
293 --output-directory $parentdir
294 done
295}
296
297
298#################################################################
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100299# Print scripts usage
300# Arguments:
301# None
302# Outputs:
303# Prints to stdout script usage
304################################################################
305usage() {
306 clear
307 echo "Usage:"
308 echo "merge -h Display this help message."
Saul Romero884d2142023-01-16 10:31:22 +0000309 echo "-j <JSON filename> JSON configuration file (info and intermediate json filenames to be merged)."
310 echo "[-l <Report path>] Coverage reports directory. Defaults to ./Coverage"
311 echo "[-w <Workspace path>] Workspace directory for source code files."
312 echo "[-o <Info filename>] Merged info file. Defaults to ./merged_coverage.info"
313 echo "[-m <JSON filename>] JSON merged SCM sources. Defaults to ./merged_scm.json"
314 echo "[-c] Flag to download/copy the source files from the JSON merged SCM into the workspace directory."
315 echo "[-g] Flag to generate local reports for each info/json instance."
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100316 echo "$help_message"
317}
318
319help_message=$(cat <<EOF
320
Saul Romero884d2142023-01-16 10:31:22 +0000321# The script merging the info files (code coverage) and json SCM sources
322# (intermediate layer) needs a JSON configuration file with the following
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100323# properties:
324# files: array of objects that describe the type of file/project to be
325# merged.
326# id: Unique identifier (project) associated to the info and
327# intermediate json files
328# config: Intermediate json file
329# type: Type of storage for the file. (http or file)
330# origin: Location (url or folder) of the file
331# info: Info file
332# type: Type of storage for the file. (http or file)
333# origin: Location (url or folder) of the file
334# Example:
335{ "files" : [
336 {
337 "id": "<project 1>",
338 "config":
339 {
340 "type": "http",
341 "origin": "<URL of json file for project 1>"
342 },
343 "info":
344 {
345 "type": "http",
346 "origin": "<URL of info file for project 1>"
347 }
348 },
349 {
350 "id": "<project 2>",
351 "config":
352 {
353 "type": "http",
354 "origin": "<URL of json file for project 2>"
355 },
356 "info":
357 {
358 "type": "http",
359 "origin": "<URL of info file for project 2>"
360 }
361 },
362 .
363 .
364 .
365 ]
366}
367EOF
368)
369
370clear
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100371LOCAL_WORKSPACE=""
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100372CLONE_SOURCES=false
Saul Romero884d2142023-01-16 10:31:22 +0000373merge_configuration_file=""
374generate_local=false
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100375# Folder to to put the reports
Saul Romero884d2142023-01-16 10:31:22 +0000376LCOV_FOLDER="./Coverage"
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100377# File name for merge coverage info
Saul Romero884d2142023-01-16 10:31:22 +0000378merged_coverage_file="./merged_coverage.info"
379merged_json_file="./merged_scm.json"
380info_files=() # Array of info files
381while getopts ":hj:o:l:w:i:cm:g" opt; do
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100382 case ${opt} in
383 h )
384 usage
385 exit 0
386 ;;
387 w )
388 LOCAL_WORKSPACE=$(cd $OPTARG; pwd)
389 ;;
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100390 c )
391 CLONE_SOURCES=true
392 ;;
393 j )
Saul Romero884d2142023-01-16 10:31:22 +0000394 merge_configuration_file=$OPTARG
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100395 ;;
396 l )
397 LCOV_FOLDER=$OPTARG
398 ;;
399 o )
Saul Romero884d2142023-01-16 10:31:22 +0000400 merged_coverage_file=$OPTARG
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100401 ;;
402 m )
Saul Romero884d2142023-01-16 10:31:22 +0000403 merged_json_file=$OPTARG
404 ;;
405 g )
406 generate_local=true
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100407 ;;
408 \? )
409 echo "Invalid option: $OPTARG" 1>&2
410 usage
411 exit -1
412 ;;
413 : )
414 echo "Invalid option: $OPTARG requires an argument" 1>&2
415 usage
416 exit -1
417 ;;
418 esac
419done
420shift $((OPTIND -1))
Saul Romero884d2142023-01-16 10:31:22 +0000421if [ -z "$merge_configuration_file" ]; then
422 echo "Merged configuration file required."
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100423 usage
424 exit -1
425fi
426if [ -z "$LOCAL_WORKSPACE" ] && [ $CLONE_SOURCES = true ]; then
Saul Romero884d2142023-01-16 10:31:22 +0000427 echo "A local workspace directory is required to clone/copy sources!"
saul-romero-arm6c40b242021-06-18 10:21:04 +0000428 exit -1
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100429fi
Saul Romero884d2142023-01-16 10:31:22 +0000430# Getting the script folder where other qa-tools script files must reside, i.e
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100431# merge.py, clone_sources.py
432DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
Saul Romero884d2142023-01-16 10:31:22 +0000433LCOV_FOLDER="$(get_abs_path $LCOV_FOLDER)"
434merged_coverage_file="$(get_abs_path $merged_coverage_file)"
435merged_json_file="$(get_abs_path $merged_json_file)"
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100436param_cloned=""
437get_info_json_files
438merge_files
439if [ $CLONE_SOURCES = true ];then
Saul Romero884d2142023-01-16 10:31:22 +0000440 clone_repos $merged_json_file
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100441fi
Saul Romero884d2142023-01-16 10:31:22 +0000442
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100443# Generate branch coverage report
Saul Romero884d2142023-01-16 10:31:22 +0000444genhtml --branch-coverage $merged_coverage_file \
Basil Eljuse4b14afb2020-09-30 13:07:23 +0100445 --output-directory $LCOV_FOLDER
Saul Romero884d2142023-01-16 10:31:22 +0000446
447if [ "$generate_local" = true ];then
448 generate_local_reports
449fi