1212import shutil
1313import shlex
1414import subprocess
15+ import platform
1516from pathlib import Path
1617from datetime import datetime
1718
2324from fosslight_util .set_log import init_log
2425from fosslight_util .timer_thread import TimerThread
2526import 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
2728from fosslight_prechecker ._precheck import run_lint as prechecker_lint
2829from fosslight_util .cover import CoverItem
2930from fosslight_util .oss_item import ScannerItem
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
124127def 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