Skip to content

Commit 501bc37

Browse files
committed
Support multi format and spdx
Signed-off-by: jiyeong.seok <[email protected]>
1 parent 7c0173b commit 501bc37

File tree

7 files changed

+108
-73
lines changed

7 files changed

+108
-73
lines changed

requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ openpyxl
44
progress
55
pyyaml
66
beautifulsoup4
7-
fosslight_util>=2.0.0
8-
fosslight_source>=2.0.0
9-
fosslight_dependency>=4.0.0
10-
fosslight_binary>=5.0.0
11-
fosslight_prechecker>=4.0.0
7+
fosslight_util>=2.1.0,<3.0.0
8+
fosslight_source>=2.1.0,<3.0.0
9+
fosslight_dependency>=4.1.0,<5.0.0
10+
fosslight_binary>=5.1.0,<6.0.0
11+
fosslight_prechecker>=4.0.0,<5.0.0

src/fosslight_scanner/_help.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
* Compare mode input file: Two FOSSLight reports (supports excel, yaml)
2626
(ex, -p {before_name}.xlsx {after_name}.xlsx)
2727
-w <link>\t\t Link to be analyzed can be downloaded by wget or git clone
28-
-f <format>\t\t FOSSLight Report file format (excel, yaml)
28+
-f <formats> [<format> ...]\t FOSSLight Report file format (excel, csv, opossum, yaml, spdx-tag, spdx-yaml, spdx-json, spdx-xml)
2929
* Compare mode result file: supports excel, json, yaml, html
30+
* Multiple formats can be specified separated by space.
3031
-e <path>\t\t Path to exclude from analysis (ex, -e {dir} {file})
3132
-o <output>\t\t Output directory or file
3233
-c <number>\t\t Number of processes to analyze source

src/fosslight_scanner/_run_compare.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def write_compared_result(output_file, compared_result, file_ext, before_f='', a
203203
def get_comparison_result_filename(output_path, output_file, output_extension, _start_time):
204204
result_file = ""
205205
compare_prefix = f"fosslight_compare_{_start_time}"
206-
if output_file != "":
206+
if output_file != '':
207207
result_file = f"{output_file}{output_extension}"
208208
else:
209209
if output_extension == XLSX_EXT or output_extension == "":
@@ -232,7 +232,7 @@ def count_compared_result(compared_result):
232232
logger.info(f"Comparison result: {count_str}")
233233

234234

235-
def run_compare(before_f, after_f, output_path, output_file, file_ext, _start_time, _output_dir):
235+
def run_compare(before_f, after_f, output_path, output_files, file_ext, _start_time, _output_dir):
236236
ret = False
237237
before_yaml = ''
238238
after_yaml = ''
@@ -254,8 +254,6 @@ def run_compare(before_f, after_f, output_path, output_file, file_ext, _start_ti
254254
tmp_a_yaml = f'{os.path.basename(after_f).rstrip(XLSX_EXT)}{YAML_EXT}'
255255
after_yaml = after_f if after_ext == YAML_EXT else os.path.join(_output_dir, tmp_a_yaml)
256256

257-
result_file = get_comparison_result_filename(output_path, output_file, file_ext, _start_time)
258-
259257
before_basepath = os.path.dirname(before_f)
260258
after_basepath = os.path.dirname(after_f)
261259
if XLSX_EXT == before_ext:
@@ -267,13 +265,17 @@ def run_compare(before_f, after_f, output_path, output_file, file_ext, _start_ti
267265
elif YAML_EXT == after_ext:
268266
after_fileitems, _, _ = parsing_yml(after_yaml, after_basepath)
269267

268+
if output_files:
269+
output_file = output_files[0]
270270
compared_result = compare_yaml(before_fileitems, after_fileitems)
271271
if compared_result != '':
272272
count_compared_result(compared_result)
273-
ret, result_file = write_compared_result(result_file, compared_result, file_ext, before_yaml, after_yaml)
274-
if ret:
275-
logger.info(f"Success to write compared result: {result_file}")
276-
else:
277-
logger.error("Fail to write compared result file.")
273+
for f_ext in file_ext:
274+
result_file = get_comparison_result_filename(output_path, output_file, f_ext, _start_time)
275+
ret, result_file = write_compared_result(result_file, compared_result, f_ext, before_yaml, after_yaml)
276+
if ret:
277+
logger.info(f"Output file: {result_file}")
278+
else:
279+
logger.error("Fail to write compared result file.")
278280

279281
return ret

src/fosslight_scanner/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ def main():
7474
dest='path', nargs='+', default="")
7575
parser.add_argument('--wget', '-w', help='Link to be analyzed',
7676
type=str, dest='link', default="")
77-
parser.add_argument('--format', '-f',
77+
parser.add_argument('--formats', '-f',
7878
help='Scanner output file format (excel,yaml), Compare mode (excel,html,yaml,json)',
79-
type=str, dest='format', default="")
79+
type=str, dest='format',nargs='*', default=[])
8080
parser.add_argument('--output', '-o', help='Output directory or file',
8181
type=str, dest='output', default="")
8282
parser.add_argument('--dependency', '-d', help='Dependency arguments',

src/fosslight_scanner/fosslight_scanner.py

Lines changed: 81 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import shutil
1313
import shlex
1414
import subprocess
15+
import platform
1516
from pathlib import Path
1617
from datetime import datetime
1718

@@ -23,7 +24,7 @@
2324
from fosslight_util.set_log import init_log
2425
from fosslight_util.timer_thread import TimerThread
2526
import fosslight_util.constant as constant
26-
from fosslight_util.output_format import check_output_format
27+
from fosslight_util.output_format import check_output_formats_v2
2728
from fosslight_prechecker._precheck import run_lint as prechecker_lint
2829
from fosslight_util.cover import CoverItem
2930
from fosslight_util.oss_item import ScannerItem
@@ -56,7 +57,7 @@
5657
]
5758

5859

59-
def run_dependency(path_to_analyze, output_file_with_path, params="", path_to_exclude=[]):
60+
def run_dependency(path_to_analyze, output_file_with_path, params="", path_to_exclude=[], formats=[]):
6061
result = []
6162

6263
package_manager = ""
@@ -99,7 +100,7 @@ def run_dependency(path_to_analyze, output_file_with_path, params="", path_to_ex
99100
output_file_with_path,
100101
pip_activate_cmd, pip_deactivate_cmd,
101102
output_custom_dir, app_name,
102-
github_token, path_to_exclude=path_to_exclude
103+
github_token, formats, True, path_to_exclude=path_to_exclude
103104
)
104105
if success:
105106
result = scan_item
@@ -114,33 +115,62 @@ def source_analysis_wrapper(*args, **kwargs):
114115
source_write_json_file = kwargs.pop('source_write_json_file', False)
115116
source_print_matched_text = kwargs.pop('source_print_matched_text', False)
116117
source_time_out = kwargs.pop('source_time_out', 120)
118+
formats = kwargs.pop('formats', [])
117119
args = list(args)
118120
args.insert(2, source_write_json_file)
119121
args.insert(5, source_print_matched_text)
122+
args.insert(6, formats)
120123

121124
return source_analysis(*args, selected_scanner=selected_scanner, time_out=source_time_out, **kwargs)
122125

123126

124127
def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
125128
run_src=True, run_bin=True, run_dep=True, run_prechecker=False,
126-
remove_src_data=True, result_log={}, output_file="",
127-
output_extension="", num_cores=-1, db_url="",
129+
remove_src_data=True, result_log={}, output_files=[],
130+
output_extensions=[], num_cores=-1, db_url="",
128131
default_oss_name="", default_oss_version="", url="",
129132
correct_mode=True, correct_fpath="", ui_mode=False, path_to_exclude=[],
130133
selected_source_scanner="all", source_write_json_file=False, source_print_matched_text=False,
131-
source_time_out=120, binary_simple=False):
134+
source_time_out=120, binary_simple=False, formats=[]):
132135
final_excel_dir = output_path
133136
success = True
134137
all_cover_items = []
135138
all_scan_item = ScannerItem(PKG_NAME, _start_time)
139+
_json_ext = '.json'
136140
if not remove_src_data:
137141
success, final_excel_dir, result_log = init(output_path)
138142

139-
if output_file == "":
140-
output_file = OUTPUT_REPORT_PREFIX + _start_time
141-
142-
if output_extension == "":
143-
output_extension = ".xlsx"
143+
if not output_files:
144+
# If -o does not contains file name, set default name
145+
while len(output_files) < len(output_extensions):
146+
output_files.append(None)
147+
to_remove = [] # elements of spdx format on windows that should be removed
148+
for i, output_extension in enumerate(output_extensions):
149+
if output_files[i] is None or output_files[i] == "":
150+
if formats:
151+
if formats[i].startswith('spdx'):
152+
if platform.system() != 'Windows':
153+
output_files[i] = f"fosslight_spdx_all_{_start_time}"
154+
else:
155+
logger.warning('spdx format is not supported on Windows. Please remove spdx from format.')
156+
to_remove.append(i)
157+
else:
158+
if output_extension == _json_ext:
159+
output_files[i] = f"fosslight_opossum_all_{_start_time}"
160+
else:
161+
output_files[i] = f"fosslight_report_all_{_start_time}"
162+
else:
163+
if output_extension == _json_ext:
164+
output_files[i] = f"fosslight_opossum_all_{_start_time}"
165+
else:
166+
output_files[i] = f"fosslight_report_all_{_start_time}"
167+
for index in sorted(to_remove, reverse=True):
168+
# remove elements of spdx format on windows
169+
del output_files[index]
170+
del output_extensions[index]
171+
del formats[index]
172+
if len(output_extensions) < 1:
173+
sys.exit(0)
144174

145175
if not correct_fpath:
146176
correct_fpath = src_path
@@ -150,21 +180,16 @@ def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
150180
abs_path = os.path.abspath(src_path)
151181

152182
if success:
153-
output_files = {"SRC": f"fosslight_src_{_start_time}{output_extension}",
154-
"BIN": f"fosslight_bin_{_start_time}{output_extension}",
155-
"DEP": f"fosslight_dep_{_start_time}{output_extension}",
156-
"PRECHECKER": f"fosslight_lint_{_start_time}.yaml"}
157183
if run_prechecker:
158-
output_prechecker = os.path.join(_output_dir, output_files["PRECHECKER"])
159184
success, result = call_analysis_api(src_path, "Prechecker Lint",
160185
-1, prechecker_lint,
161-
abs_path, False, output_prechecker,
186+
abs_path, False, _output_dir,
162187
exclude_path=path_to_exclude)
163188

164189
if run_src:
165190
try:
166191
if fosslight_source_installed:
167-
src_output = os.path.join(_output_dir, output_files["SRC"])
192+
src_output = _output_dir
168193
success, result = call_analysis_api(
169194
src_path,
170195
"Source Analysis",
@@ -177,17 +202,17 @@ def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
177202
selected_scanner=selected_source_scanner,
178203
source_write_json_file=source_write_json_file,
179204
source_print_matched_text=source_print_matched_text,
180-
source_time_out=source_time_out
205+
source_time_out=source_time_out,
206+
formats=formats
181207
)
182208
if success:
183209
all_scan_item.file_items.update(result[2].file_items)
184210
all_cover_items.append(result[2].cover)
185211

186212
else: # Run fosslight_source by using docker image
187-
src_output = os.path.join("output", output_files["SRC"])
188213
output_rel_path = os.path.relpath(abs_path, os.getcwd())
189214
command = shlex.quote(f"docker run -it -v {_output_dir}:/app/output "
190-
f"fosslight -p {output_rel_path} -o {src_output}")
215+
f"fosslight -p {output_rel_path} -o output")
191216
if path_to_exclude:
192217
command += f" -e {' '.join(path_to_exclude)}"
193218
command_result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
@@ -200,29 +225,26 @@ def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
200225
success, result = call_analysis_api(src_path, "Binary Analysis",
201226
1, binary_analysis.find_binaries,
202227
abs_path,
203-
os.path.join(_output_dir, output_files["BIN"]),
204-
"", db_url, binary_simple,
228+
_output_dir,
229+
formats, db_url, binary_simple,
205230
correct_mode, correct_fpath,
206231
path_to_exclude=path_to_exclude)
207232
if success:
208233
all_scan_item.file_items.update(result.file_items)
209234
all_cover_items.append(result.cover)
210235

211236
if run_dep:
212-
dep_scanitem = run_dependency(src_path, os.path.join(_output_dir, output_files["DEP"]),
213-
dep_arguments, path_to_exclude)
237+
dep_scanitem = run_dependency(src_path, _output_dir,
238+
dep_arguments, path_to_exclude, formats)
214239
all_scan_item.file_items.update(dep_scanitem.file_items)
215240
all_cover_items.append(dep_scanitem.cover)
216-
217241
else:
218242
return
219243

220244
except Exception as ex:
221245
logger.error(f"Scanning: {ex}")
222246

223247
try:
224-
output_file_without_ext = os.path.join(final_excel_dir, output_file)
225-
final_report = f"{output_file_without_ext}{output_extension}"
226248
cover = CoverItem(tool_name=PKG_NAME,
227249
start_time=_start_time,
228250
input_path=abs_path,
@@ -239,17 +261,32 @@ def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
239261

240262
if remove_src_data:
241263
all_scan_item = update_oss_item(all_scan_item, default_oss_name, default_oss_version, url)
242-
success, err_msg, final_report = write_output_file(output_file_without_ext, output_extension, all_scan_item)
264+
265+
combined_paths_and_files = [os.path.join(final_excel_dir, file) for file in output_files]
266+
results = []
267+
final_reports = []
268+
for combined_path_and_file, output_extension, output_format in zip(combined_paths_and_files, output_extensions, formats):
269+
results.append(write_output_file(combined_path_and_file, output_extension, all_scan_item, {}, {}, output_format))
270+
for success, msg, result_file in results:
271+
if success:
272+
final_reports.append(result_file)
273+
logger.info(f"Output file: {result_file}")
274+
else:
275+
logger.error(f"Fail to generate result file {result_file}. msg:({msg})")
276+
243277
if success:
244-
if os.path.isfile(final_report):
245-
logger.info(f'Generated the result file: {final_report}')
246-
result_log["Output File"] = final_report
278+
if final_reports:
279+
logger.info(f'Generated the result file: {", ".join(final_reports)}')
280+
result_log["Output File"] = ', '.join(final_reports)
247281
else:
248282
result_log["Output File"] = 'Nothing is detected from the scanner so output file is not generated.'
249-
else:
250-
logger.error(f"Fail to generate a result file({final_report}): {err_msg}")
251283

252284
if ui_mode:
285+
if output_files:
286+
output_file = output_files[0]
287+
else:
288+
output_file = OUTPUT_REPORT_PREFIX + _start_time
289+
output_file_without_ext = os.path.join(final_excel_dir, output_file)
253290
ui_mode_report = f"{output_file_without_ext}.json"
254291
success, err_msg = create_scancodejson(all_scan_item, ui_mode_report, src_path)
255292
if success and os.path.isfile(ui_mode_report):
@@ -328,7 +365,7 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
328365
source_time_out=120, binary_simple=False):
329366
global _executed_path, _start_time
330367

331-
output_file = ""
368+
output_files = []
332369
default_oss_name = ""
333370
default_oss_version = ""
334371
src_path = ""
@@ -352,7 +389,7 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
352389
logger.error("Enter two FOSSLight report file with 'p' option.")
353390
return False
354391
else:
355-
CUSTOMIZED_FORMAT = {'excel': '.xlsx', 'yaml': '.yaml'}
392+
CUSTOMIZED_FORMAT = {}
356393
if isinstance(path_arg, list):
357394
if len(path_arg) == 1:
358395
src_path = path_arg[0]
@@ -370,10 +407,12 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
370407
else:
371408
logger.warning(f"Cannot analyze with multiple path: {path_arg}")
372409

373-
success, msg, output_path, output_file, output_extension = check_output_format(output_file_or_dir, file_format,
374-
CUSTOMIZED_FORMAT)
410+
success, msg, output_path, output_files, output_extensions, formats = check_output_formats_v2(output_file_or_dir, file_format,
411+
CUSTOMIZED_FORMAT)
375412
if output_path == "":
376413
output_path = _executed_path
414+
else:
415+
output_path = os.path.abspath(output_path)
377416

378417
if not success:
379418
logger.error(msg)
@@ -392,7 +431,7 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
392431
ret, final_excel_dir, result_log = init(output_path)
393432

394433
run_compare(os.path.join(_executed_path, before_comp_f), os.path.join(_executed_path, after_comp_f),
395-
final_excel_dir, output_file, output_extension, _start_time, _output_dir)
434+
final_excel_dir, output_files, output_extensions, _start_time, _output_dir)
396435
else:
397436
run_src = False
398437
run_bin = False
@@ -430,22 +469,15 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
430469
remove_downloaded_source = True
431470
success, src_path, default_oss_name, default_oss_version = download_source(url_to_analyze, output_path)
432471

433-
if output_extension == ".yaml":
434-
correct_mode = False
435-
correct_fpath = ""
436-
else:
437-
if not correct_fpath:
438-
correct_fpath = src_path
439-
440472
if src_path != "":
441473
run_scanner(src_path, dep_arguments, output_path, keep_raw_data,
442474
run_src, run_bin, run_dep, run_prechecker,
443-
remove_downloaded_source, {}, output_file,
444-
output_extension, num_cores, db_url,
475+
remove_downloaded_source, {}, output_files,
476+
output_extensions, num_cores, db_url,
445477
default_oss_name, default_oss_version, url_to_analyze,
446478
correct_mode, correct_fpath, ui_mode, path_to_exclude,
447479
selected_source_scanner, source_write_json_file, source_print_matched_text, source_time_out,
448-
binary_simple)
480+
binary_simple, formats)
449481

450482
if extract_folder:
451483
shutil.rmtree(extract_folder)

0 commit comments

Comments
 (0)