fosslight-dependency 4.1.18__py3-none-any.whl → 4.1.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -92,11 +92,11 @@ def analyze_dependency(package_manager_name, input_dir, output_dir, pip_activate
92
92
  package_manager.parse_oss_information_for_pnpm()
93
93
  package_dep_item_list.extend(package_manager.dep_items)
94
94
  if ret:
95
- logger.warning(f"### Complete to analyze: {package_manager_name}")
95
+ logger.warning(f"### Complete to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
96
96
  if package_manager.cover_comment:
97
97
  cover_comment = package_manager.cover_comment
98
98
  else:
99
- logger.error(f"### Fail to analyze: {package_manager_name}")
99
+ logger.error(f"### Fail to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
100
100
 
101
101
  del package_manager
102
102
 
@@ -46,6 +46,7 @@ _HELP_MESSAGE_DEPENDENCY = """
46
46
  \t\t\t\t\t--graph-path option is required
47
47
  --direct\t\t\t Print the direct/transitive dependency type in comment.
48
48
  \t\tChoice 'True' or 'False'. (default:True)
49
+ -r\t\t\t\t Recursive mode. Scan all subdirectories for manifest files.
49
50
  --notice\t\t\t Print the open source license notice text.
50
51
 
51
52
  Required only for swift, carthage
@@ -108,6 +108,7 @@ class PackageManager:
108
108
  cmd_gradle = "./gradlew"
109
109
  else:
110
110
  ret_task = False
111
+ self.set_direct_dependencies(False)
111
112
  logger.warning('No gradlew file exists (Skip to find dependencies relationship.).')
112
113
  if ret_plugin:
113
114
  logger.warning('Also it cannot run android-dependency-scanning plugin.')
@@ -153,6 +154,7 @@ class PackageManager:
153
154
  shutil.move(module_gradle_backup, module_build_gradle)
154
155
  if os.path.isfile(self.input_file_name):
155
156
  logger.info(f'Found {self.input_file_name}, skip to run plugin.')
157
+ self.set_direct_dependencies(False)
156
158
  ret_task = True
157
159
  return ret_task
158
160
 
@@ -31,12 +31,12 @@ class Npm(PackageManager):
31
31
  super().__init__(self.package_manager_name, self.dn_url, input_dir, output_dir)
32
32
 
33
33
  def __del__(self):
34
- if os.path.isfile(self.input_file_name):
35
- os.remove(self.input_file_name)
34
+ if os.path.isfile(os.path.join(self.input_dir, self.input_file_name)):
35
+ os.remove(os.path.join(self.input_dir, self.input_file_name))
36
36
  if self.flag_tmp_node_modules:
37
- shutil.rmtree(node_modules, ignore_errors=True)
38
- if os.path.exists(self.tmp_custom_json):
39
- os.remove(self.tmp_custom_json)
37
+ shutil.rmtree(os.path.join(self.input_dir, node_modules), ignore_errors=True)
38
+ if os.path.exists(os.path.join(self.input_dir, self.tmp_custom_json)):
39
+ os.remove(os.path.join(self.input_dir, self.tmp_custom_json))
40
40
 
41
41
  def run_plugin(self):
42
42
  ret = self.start_license_checker()
@@ -122,9 +122,9 @@ class Npm(PackageManager):
122
122
  if len(rel_json) < 1:
123
123
  ret = False
124
124
  else:
125
- self.package_name = f'{rel_json[_name]}({rel_json[_version]})'
125
+ self.package_name = f'{rel_json[_name]}({rel_json.get(_version, "")})'
126
126
  if _dependencies in rel_json:
127
- self.parse_rel_dependencies(rel_json[_name], rel_json[_version], rel_json[_dependencies])
127
+ self.parse_rel_dependencies(rel_json[_name], rel_json.get(_version, ""), rel_json[_dependencies])
128
128
  except Exception as e:
129
129
  ret = False
130
130
  err_msg = e
@@ -178,7 +178,7 @@ class Npm(PackageManager):
178
178
 
179
179
  oss_item.download_location = f"{self.dn_url}{oss_init_name}/v/{oss_item.version}"
180
180
  dn_loc = f"{self.dn_url}{oss_init_name}"
181
- dep_item.purl = get_url_to_purl(dn_loc, self.package_manager_name)
181
+ dep_item.purl = get_url_to_purl(oss_item.download_location, self.package_manager_name)
182
182
  purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
183
183
  if d[_repository]:
184
184
  dn_loc = d[_repository]
@@ -13,7 +13,7 @@ import re
13
13
  import fosslight_util.constant as constant
14
14
  import fosslight_dependency.constant as const
15
15
  from fosslight_dependency._package_manager import PackageManager
16
- from fosslight_dependency._package_manager import check_license_name, get_url_to_purl
16
+ from fosslight_dependency._package_manager import get_url_to_purl, check_license_name
17
17
  from fosslight_dependency.dependency_item import DependencyItem, change_dependson_to_purl
18
18
  from fosslight_util.oss_item import OssItem
19
19
 
@@ -26,7 +26,6 @@ class Pypi(PackageManager):
26
26
  dn_url = 'https://pypi.org/project/'
27
27
  venv_tmp_dir = 'venv_osc_dep_tmp'
28
28
  tmp_file_name = "tmp_pip_license_output.json"
29
- tmp_pip_license_info_file_name = "tmp_pip_license_info_output.json"
30
29
  tmp_deptree_file = "tmp_pipdeptree.json"
31
30
  pip_activate_cmd = ''
32
31
  pip_deactivate_cmd = ''
@@ -41,9 +40,6 @@ class Pypi(PackageManager):
41
40
  if os.path.isfile(self.tmp_file_name):
42
41
  os.remove(self.tmp_file_name)
43
42
 
44
- if os.path.isfile(self.tmp_pip_license_info_file_name):
45
- os.remove(self.tmp_pip_license_info_file_name)
46
-
47
43
  shutil.rmtree(self.venv_tmp_dir, ignore_errors=True)
48
44
 
49
45
  if os.path.isfile(self.tmp_deptree_file):
@@ -73,7 +69,7 @@ class Pypi(PackageManager):
73
69
  ret = self.create_virtualenv()
74
70
 
75
71
  if ret:
76
- ret = self.start_pip_licenses()
72
+ ret = self.start_pip_inspect()
77
73
 
78
74
  return ret
79
75
 
@@ -103,7 +99,7 @@ class Pypi(PackageManager):
103
99
  activate_cmd = os.path.join(self.venv_tmp_dir, "Scripts", "activate.bat")
104
100
  cmd_separator = "&"
105
101
  else:
106
- create_venv_cmd = f"python3 -m venv {self.venv_tmp_dir}"
102
+ create_venv_cmd = f"virtualenv -p python3 {self.venv_tmp_dir}"
107
103
  activate_cmd = ". " + os.path.join(venv_path, "bin", "activate")
108
104
  cmd_separator = ";"
109
105
 
@@ -117,11 +113,12 @@ class Pypi(PackageManager):
117
113
  return False
118
114
 
119
115
  deactivate_cmd = "deactivate"
116
+ pip_upgrade_cmd = "pip install --upgrade pip"
120
117
 
121
118
  self.set_pip_activate_cmd(activate_cmd)
122
119
  self.set_pip_deactivate_cmd(deactivate_cmd)
123
120
 
124
- cmd_list = [create_venv_cmd, activate_cmd, install_cmd, deactivate_cmd]
121
+ cmd_list = [create_venv_cmd, activate_cmd, install_cmd, pip_upgrade_cmd, deactivate_cmd]
125
122
  cmd = cmd_separator.join(cmd_list)
126
123
 
127
124
  try:
@@ -139,9 +136,9 @@ class Pypi(PackageManager):
139
136
  try:
140
137
  if (not ret) and (self.platform != const.WINDOWS):
141
138
  ret = True
142
- create_venv_cmd = f"virtualenv -p python3 {self.venv_tmp_dir}"
139
+ create_venv_cmd = f"python3 -m venv {self.venv_tmp_dir}"
143
140
 
144
- cmd_list = [create_venv_cmd, activate_cmd, install_cmd, deactivate_cmd]
141
+ cmd_list = [create_venv_cmd, activate_cmd, install_cmd, pip_upgrade_cmd, deactivate_cmd]
145
142
  cmd = cmd_separator.join(cmd_list)
146
143
  cmd_ret = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE)
147
144
  if cmd_ret.returncode != 0:
@@ -160,14 +157,9 @@ class Pypi(PackageManager):
160
157
 
161
158
  return ret
162
159
 
163
- def start_pip_licenses(self):
160
+ def start_pip_inspect(self):
164
161
  ret = True
165
- pip_licenses = 'pip-licenses'
166
- prettytable = 'prettytable'
167
- wcwidth = 'wcwidth'
168
162
  pipdeptree = 'pipdeptree'
169
- pip_licenses_default_options = ' --from=mixed --with-url --format=json --with-license-file'
170
- pip_licenses_system_option = ' --with-system -p '
171
163
  tmp_pip_list = "tmp_list.txt"
172
164
  python_cmd = "python -m"
173
165
 
@@ -193,11 +185,21 @@ class Pypi(PackageManager):
193
185
  command_list = [activate_command, pip_list_command, deactivate_command]
194
186
  command = command_separator.join(command_list)
195
187
 
188
+ exists_pipdeptree = False
196
189
  try:
197
190
  cmd_ret = subprocess.call(command, shell=True)
198
191
  if cmd_ret != 0:
199
192
  ret = False
200
193
  err_msg = f"cmd ret code({cmd_ret})"
194
+ else:
195
+ if os.path.isfile(tmp_pip_list):
196
+ with open(tmp_pip_list, 'r', encoding='utf-8') as pip_list_file:
197
+ for pip_list in pip_list_file.readlines():
198
+ pip_list_name = pip_list.split('==')[0]
199
+ if pip_list_name == pipdeptree:
200
+ exists_pipdeptree = True
201
+ break
202
+ os.remove(tmp_pip_list)
201
203
  except Exception as e:
202
204
  ret = False
203
205
  err_msg = str(e)
@@ -206,63 +208,11 @@ class Pypi(PackageManager):
206
208
  logger.error(f"Failed to freeze dependencies ({command}): {err_msg})")
207
209
  return False
208
210
 
209
- exists_pip_licenses = False
210
- exists_prettytable = False
211
- exists_wcwidth = False
212
- pip_license_pkg_list = []
213
- uninstall_pkg_list = []
214
- exists_pipdeptree = False
215
-
216
- if os.path.isfile(tmp_pip_list):
217
- try:
218
- with open(tmp_pip_list, 'r', encoding='utf-8') as pip_list_file:
219
- for pip_list in pip_list_file.readlines():
220
- pip_list_name = pip_list.split('==')[0]
221
- if pip_list_name == pip_licenses:
222
- exists_pip_licenses = True
223
- if pip_list_name == prettytable:
224
- exists_prettytable = True
225
- if pip_list_name == wcwidth:
226
- exists_wcwidth = True
227
- if pip_list_name == pipdeptree:
228
- exists_pipdeptree = True
229
- os.remove(tmp_pip_list)
230
- except Exception as e:
231
- logger.error(f"Failed to read freezed package list file: {e}")
232
- return False
233
- if exists_pip_licenses:
234
- pip_license_pkg_list.append(pip_licenses)
235
- else:
236
- uninstall_pkg_list.append(pip_licenses)
237
- if exists_prettytable:
238
- pip_license_pkg_list.append(prettytable)
239
- else:
240
- uninstall_pkg_list.append(prettytable)
241
- if exists_wcwidth:
242
- pip_license_pkg_list.append(wcwidth)
243
- else:
244
- uninstall_pkg_list.append(wcwidth)
245
-
246
211
  command_list = []
247
212
  command_list.append(activate_command)
248
- if not exists_pip_licenses:
249
- install_pip_command = f"{python_cmd} pip install {pip_licenses}"
250
- command_list.append(install_pip_command)
251
-
252
- pip_licenses_command = f"{pip_licenses}{pip_licenses_default_options} > {self.tmp_file_name}"
253
- command_list.append(pip_licenses_command)
254
-
255
- if len(pip_license_pkg_list) != 0:
256
- pip_licenses_info_command = f"{pip_licenses}{pip_licenses_default_options}{pip_licenses_system_option}"
257
- pip_licenses_info_command += " ".join(pip_license_pkg_list)
258
-
259
- pip_licenses_info_command += f" > {self.tmp_pip_license_info_file_name}"
260
- command_list.append(pip_licenses_info_command)
261
213
 
262
- if len(uninstall_pkg_list) > 0:
263
- uninstall_pip_command = f"{python_cmd} pip uninstall -y "
264
- uninstall_pip_command += ' '.join(uninstall_pkg_list)
265
- command_list.append(uninstall_pip_command)
214
+ pip_inspect_command = f"{python_cmd} pip inspect > {self.tmp_file_name}"
215
+ command_list.append(pip_inspect_command)
266
216
 
267
217
  if not exists_pipdeptree:
268
218
  install_deptree_command = f"{python_cmd} pip install {pipdeptree}"
@@ -270,30 +220,37 @@ class Pypi(PackageManager):
270
220
  uninstall_deptree_command = f"{python_cmd} pip uninstall -y {pipdeptree}"
271
221
  pipdeptree_command = f"{pipdeptree} --json-tree -e 'pipdeptree,pip,wheel,setuptools' > {self.tmp_deptree_file}"
272
222
  command_list.append(pipdeptree_command)
273
- command_list.append(uninstall_deptree_command)
223
+
224
+ if not exists_pipdeptree:
225
+ command_list.append(uninstall_deptree_command)
226
+
274
227
  command_list.append(deactivate_command)
275
228
  command = command_separator.join(command_list)
276
229
 
277
230
  try:
278
231
  cmd_ret = subprocess.call(command, shell=True)
279
232
  if cmd_ret == 0:
280
- self.append_input_package_list_file(self.tmp_file_name)
281
- with open(self.tmp_file_name, 'r', encoding='utf-8') as json_f:
282
- json_data = json.load(json_f)
283
- for d in json_data:
284
- self.total_dep_list.append(re.sub(r"[-_.]+", "-", d['Name']).lower())
285
- if len(pip_license_pkg_list) != 0:
286
- self.append_input_package_list_file(self.tmp_pip_license_info_file_name)
287
- with open(self.tmp_pip_license_info_file_name, 'r', encoding='utf-8') as json_f:
288
- json_data = json.load(json_f)
289
- for d in json_data:
290
- self.total_dep_list.append(re.sub(r"[-_.]+", "-", d['Name']).lower())
233
+ if os.path.exists(self.tmp_file_name):
234
+ self.append_input_package_list_file(self.tmp_file_name)
235
+
236
+ with open(self.tmp_file_name, 'r', encoding='utf-8') as json_f:
237
+ inspect_data = json.load(json_f)
238
+ for package in inspect_data.get('installed', []):
239
+ metadata = package.get('metadata', {})
240
+ package_name = metadata.get('name', '')
241
+ if package_name:
242
+ if package_name in ['pip', 'setuptools']:
243
+ continue
244
+ self.total_dep_list.append(re.sub(r"[-_.]+", "-", package_name).lower())
245
+ else:
246
+ logger.error(f"pip inspect output file not found: {self.tmp_file_name}")
247
+ ret = False
291
248
  else:
292
249
  logger.error(f"Failed to run command: {command}")
293
250
  ret = False
294
251
  except Exception as e:
295
252
  ret = False
296
- logger.error(f"Failed to install/uninstall pypi packages: {e}")
253
+ logger.error(f"Failed to get package information using pip inspect: {e}")
297
254
 
298
255
  return ret
299
256
 
@@ -302,25 +259,70 @@ class Pypi(PackageManager):
302
259
  try:
303
260
  oss_init_name = ''
304
261
  with open(f_name, 'r', encoding='utf-8') as json_file:
305
- json_data = json.load(json_file)
262
+ inspect_data = json.load(json_file)
306
263
 
307
- for d in json_data:
264
+ for package in inspect_data.get('installed', []):
308
265
  dep_item = DependencyItem()
309
266
  oss_item = OssItem()
310
- oss_init_name = d['Name']
267
+ metadata = package.get('metadata', {})
268
+ if not metadata:
269
+ continue
270
+
271
+ oss_init_name = metadata.get('name', '')
311
272
  oss_init_name = re.sub(r"[-_.]+", "-", oss_init_name).lower()
273
+ if oss_init_name not in self.total_dep_list:
274
+ continue
312
275
  oss_item.name = f"{self.package_manager_name}:{oss_init_name}"
313
- license_name = check_UNKNOWN(d['License'])
314
- oss_item.homepage = check_UNKNOWN(d['URL'])
315
- oss_item.version = d['Version']
276
+ oss_item.version = metadata.get('version', '')
277
+
278
+ # license_expression > license > classifier
279
+ license_info = check_UNKNOWN(metadata.get('license_expression', ''))
280
+ if not license_info:
281
+ license_info = metadata.get('license', '')
282
+ if '\n' in license_info:
283
+ license_info = check_UNKNOWN(check_license_name(license_info))
284
+ if not license_info:
285
+ classifiers = metadata.get('classifier', [])
286
+ license_classifiers = [c for c in classifiers if c.startswith('License ::')]
287
+ if license_classifiers:
288
+ license_info_l = []
289
+ for license_classifier in license_classifiers:
290
+ if license_classifier.startswith('License :: OSI Approved ::'):
291
+ license_info_l.append(license_classifier.split('::')[-1].strip())
292
+ break
293
+ license_info = ','.join(license_info_l)
294
+ license_name = check_UNKNOWN(license_info)
295
+ if license_name:
296
+ license_name = license_name.replace(';', ',')
297
+ oss_item.license = license_name
298
+
316
299
  oss_item.download_location = f"{self.dn_url}{oss_init_name}/{oss_item.version}"
300
+
301
+ # project_url 'source' > download_url > project_url 'homepage' > homepage
302
+ homepage_url = check_UNKNOWN(metadata.get('home_page', ''))
303
+ download_url = check_UNKNOWN(metadata.get('download_url', ''))
304
+ project_urls = check_UNKNOWN(metadata.get('project_url', []))
305
+ if project_urls:
306
+ priority_order = ['source', 'repository', 'github', 'code', 'source code', 'homepage']
307
+ for priority in priority_order:
308
+ for url_entry in project_urls:
309
+ url_entry_lower = url_entry.lower()
310
+ if url_entry_lower.startswith(priority):
311
+ download_url = url_entry.split(', ')[-1]
312
+ break
313
+ if download_url:
314
+ break
315
+ oss_item.homepage = download_url or homepage_url or f"{self.dn_url}{oss_init_name}"
316
+
317
317
  dep_item.purl = get_url_to_purl(oss_item.download_location, self.package_manager_name)
318
318
  purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
319
- if license_name is not None:
320
- license_name = license_name.replace(';', ',')
321
- else:
322
- license_name = check_license_name(d['LicenseFile'], True)
323
- oss_item.license = license_name
319
+
320
+ direct_url = package.get('direct_url', {})
321
+ if direct_url:
322
+ oss_item.download_location = direct_url.get('url', '')
323
+ oss_item.homepage = oss_item.download_location
324
+ if not package.get('installer', ''):
325
+ oss_item.download_location = oss_item.homepage
324
326
 
325
327
  if oss_init_name == self.package_name:
326
328
  oss_item.comment = 'root package'
@@ -30,7 +30,7 @@ EXTENDED_HEADER = {_sheet_name: ['ID', 'Package URL', 'OSS Name',
30
30
  'OSS Version', 'License', 'Download Location',
31
31
  'Homepage', 'Copyright Text', 'Exclude',
32
32
  'Comment', 'Depends On']}
33
- _exclude_dir = ['node_moduels', 'venv']
33
+ _exclude_dir = ['node_modules', 'venv', 'Pods', 'Carthage']
34
34
 
35
35
 
36
36
  def get_terminal_size():
@@ -50,7 +50,7 @@ def paginate_file(file_path):
50
50
  input("Press Enter to see the next page...")
51
51
 
52
52
 
53
- def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[]):
53
+ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[], recursive=False):
54
54
  ret = True
55
55
  if not manifest_file_name:
56
56
  for value in const.SUPPORT_PACKAE.values():
@@ -62,9 +62,8 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
62
62
  found_manifest_file = []
63
63
  suggested_files = []
64
64
  for parent, dirs, files in os.walk(input_dir):
65
- if len(files) < 1:
66
- continue
67
- if os.path.basename(parent) in _exclude_dir:
65
+ parent_parts = parent.split(os.sep)
66
+ if any(ex_dir in parent_parts for ex_dir in _exclude_dir):
68
67
  continue
69
68
  if os.path.abspath(parent) in abs_path_to_exclude:
70
69
  continue
@@ -75,7 +74,8 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
75
74
  for exclude_path in abs_path_to_exclude):
76
75
  continue
77
76
  if file in manifest_file_name:
78
- found_manifest_file.append(file)
77
+ path_with_filename = os.path.join(parent, file)
78
+ found_manifest_file.append(path_with_filename)
79
79
  if file in const.SUGGESTED_PACKAGE.keys():
80
80
  suggested_files.append(os.path.join(parent, file))
81
81
  for dir in dirs:
@@ -84,31 +84,32 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
84
84
  if len(manifest_l) > 1:
85
85
  if manifest_l[0] == dir:
86
86
  if os.path.exists(os.path.join(parent, manifest_f)):
87
- found_manifest_file.append(manifest_f)
88
- if len(found_manifest_file) > 0:
89
- input_dir = parent
90
- break
91
- found_package_manager = defaultdict(list)
92
- for f_idx in found_manifest_file:
87
+ found_manifest_file.append(os.path.join(parent, manifest_f))
88
+ if not recursive:
89
+ if len(found_manifest_file) > 0:
90
+ input_dir = parent
91
+ break
92
+
93
+ found_package_manager = defaultdict(lambda: defaultdict(list))
94
+ for f_with_path in found_manifest_file:
95
+ f_name = os.path.basename(f_with_path)
96
+ dir_path = os.path.dirname(f_with_path)
93
97
  for key, value in const.SUPPORT_PACKAE.items():
94
98
  if isinstance(value, list):
95
- for v in value:
96
- if f_idx == v:
97
- if key in found_package_manager.keys():
98
- found_package_manager[key].append(f_idx)
99
- else:
100
- found_package_manager[key] = [f_idx]
99
+ if f_name in value:
100
+ found_package_manager[key][dir_path].append(f_name)
101
101
  else:
102
- if value == f_idx:
103
- found_package_manager[key] = [f_idx]
102
+ if f_name == value:
103
+ found_package_manager[key][dir_path].append(f_name)
104
+ found_package_manager = {k: dict(v) for k, v in found_package_manager.items()}
104
105
 
105
106
  # both npm and pnpm are detected, remove npm.
106
- if 'npm' in found_package_manager and 'pnpm' in found_package_manager:
107
+ if 'npm' in found_package_manager.keys() and 'pnpm' in found_package_manager.keys():
107
108
  del found_package_manager['npm']
108
109
  if len(found_package_manager) >= 1:
109
- manifest_file_w_path = [os.path.join(input_dir, file) for pkg, files in found_package_manager.items() for file in files]
110
- logger.info(f"Found the manifest file({','.join(manifest_file_w_path)}) automatically.")
111
- logger.warning(f"### Set Package Manager = {', '.join(found_package_manager.keys())}")
110
+ log_lines = ["\nDetected Manifest Files automatically"]
111
+ log_lines = print_package_info(found_package_manager, log_lines)
112
+ logger.info('\n'.join(log_lines))
112
113
  else:
113
114
  ret = False
114
115
  logger.info("Cannot find the manifest file.")
@@ -116,10 +117,20 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
116
117
  return ret, found_package_manager, input_dir, suggested_files
117
118
 
118
119
 
120
+ def print_package_info(success_pm, log_lines):
121
+ if success_pm:
122
+ for pm, dir_dict in success_pm.items():
123
+ log_lines.append(f"- {pm}:")
124
+ for path, files in dir_dict.items():
125
+ file_list = ', '.join(files)
126
+ log_lines.append(f" {path}: {file_list}")
127
+ return log_lines
128
+
129
+
119
130
  def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='', pip_activate_cmd='',
120
131
  pip_deactivate_cmd='', output_custom_dir='', app_name=const.default_app_name,
121
132
  github_token='', formats=[], direct=True, path_to_exclude=[], graph_path='',
122
- graph_size=(600, 600)):
133
+ graph_size=(600, 600), recursive=False):
123
134
  global logger
124
135
 
125
136
  ret = True
@@ -217,9 +228,8 @@ def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='',
217
228
  try:
218
229
  ret, found_package_manager, input_dir, suggested_files = find_package_manager(input_dir,
219
230
  abs_path_to_exclude,
220
- manifest_file_name)
221
- if ret:
222
- os.chdir(input_dir)
231
+ manifest_file_name,
232
+ recursive)
223
233
  except Exception as e:
224
234
  if autodetect:
225
235
  logger.error(f'Fail to find package manager: {e}')
@@ -228,7 +238,7 @@ def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='',
228
238
  if not ret:
229
239
  if not autodetect:
230
240
  logger.info('Try to analyze dependency without manifest file. (Manual mode)')
231
- found_package_manager[package_manager] = []
241
+ found_package_manager[package_manager] = {}
232
242
  else:
233
243
  ret = False
234
244
  if suggested_files:
@@ -243,40 +253,64 @@ def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='',
243
253
  else:
244
254
  scan_item.set_cover_comment("No Package manager detected.")
245
255
 
246
- pass_key = 'PASS'
247
- success_pm = []
248
- fail_pm = []
256
+ pass_key = ['PASS']
257
+ success_pm = defaultdict(lambda: defaultdict(list))
258
+ fail_pm = defaultdict(lambda: defaultdict(list))
249
259
  cover_comment = ''
250
- for pm, manifest_file_name in found_package_manager.items():
251
- if manifest_file_name == pass_key:
252
- continue
253
- ret, package_dep_item_list, cover_comment = analyze_dependency(pm, input_dir, output_path,
254
- pip_activate_cmd, pip_deactivate_cmd,
255
- output_custom_dir, app_name, github_token,
256
- manifest_file_name, direct)
257
- if ret:
258
- success_pm.append(f"{pm} ({', '.join(manifest_file_name)})")
259
- scan_item.append_file_items(package_dep_item_list)
260
- if pm == const.GRADLE:
261
- if const.ANDROID in found_package_manager.keys():
262
- found_package_manager[const.ANDROID] = pass_key
263
- if f"{const.ANDROID} ({', '.join(manifest_file_name)})" in fail_pm:
264
- fail_pm.remove(f"{const.ANDROID} ({', '.join(manifest_file_name)})")
265
- elif pm == const.ANDROID:
266
- if const.GRADLE in found_package_manager.keys():
267
- found_package_manager[const.GRADLE] = pass_key
268
- if f"{const.GRADLE} ({', '.join(manifest_file_name)})" in fail_pm:
269
- fail_pm.remove(f"{const.GRADLE} ({', '.join(manifest_file_name)})")
260
+ for pm, manifest_file_name_list in found_package_manager.items():
261
+ if not manifest_file_name_list and not autodetect:
262
+ ret, package_dep_item_list, cover_comment = analyze_dependency(pm, input_dir, output_path,
263
+ pip_activate_cmd, pip_deactivate_cmd,
264
+ output_custom_dir, app_name, github_token,
265
+ [], direct)
266
+ if ret:
267
+ success_pm[pm][input_dir].extend(['manual mode (-m option)'])
268
+ scan_item.append_file_items(package_dep_item_list)
269
+ else:
270
+ fail_pm[pm][input_dir].extend(['manual mode (-m option)'])
270
271
  else:
271
- fail_pm.append(f"{pm} ({', '.join(manifest_file_name)})")
272
+ for manifest_dir, manifest_file_name in manifest_file_name_list.items():
273
+ input_dir = manifest_dir
274
+ if manifest_file_name == pass_key:
275
+ continue
276
+ os.chdir(input_dir)
277
+ ret, package_dep_item_list, cover_comment = analyze_dependency(pm, input_dir, output_path,
278
+ pip_activate_cmd, pip_deactivate_cmd,
279
+ output_custom_dir, app_name, github_token,
280
+ manifest_file_name, direct)
281
+ if ret:
282
+ success_pm[pm][input_dir].extend(manifest_file_name)
283
+ scan_item.append_file_items(package_dep_item_list)
284
+
285
+ dup_pm = None
286
+ if pm == const.GRADLE and const.ANDROID in found_package_manager:
287
+ dup_pm = const.ANDROID
288
+ elif pm == const.ANDROID and const.GRADLE in found_package_manager:
289
+ dup_pm = const.GRADLE
290
+
291
+ if dup_pm:
292
+ if dup_pm in fail_pm and input_dir in fail_pm[dup_pm]:
293
+ fail_pm[dup_pm].pop(input_dir, None)
294
+ if not fail_pm[dup_pm]:
295
+ fail_pm.pop(dup_pm, None)
296
+ else:
297
+ found_package_manager[dup_pm][manifest_dir] = pass_key
298
+ else:
299
+ fail_pm[pm][input_dir].extend(manifest_file_name)
272
300
 
301
+ success_pm = {k: dict(v) for k, v in success_pm.items()}
302
+ fail_pm = {k: dict(v) for k, v in fail_pm.items()}
273
303
  if len(found_package_manager.keys()) > 0:
274
304
  if len(success_pm) > 0:
275
- scan_item.set_cover_comment(f"Analyzed Package manager: {', '.join(success_pm)}")
305
+ log_lines = ["Success to analyze:"]
306
+ log_lines = print_package_info(success_pm, log_lines)
307
+ scan_item.set_cover_comment('\n'.join(log_lines))
276
308
  if len(fail_pm) > 0:
277
- info_msg = 'Check log file(fosslight_log*.txt) ' \
278
- 'and https://fosslight.org/fosslight-guide-en/scanner/3_dependency.html#-prerequisite.'
279
- scan_item.set_cover_comment(f"Analysis failed Package manager: {', '.join(fail_pm)} ({info_msg})")
309
+ log_lines = ["Fail to analyze:"]
310
+ log_lines = print_package_info(fail_pm, log_lines)
311
+ scan_item.set_cover_comment('\n'.join(log_lines))
312
+ scan_item.set_cover_comment('Check log file(fosslight_log*.txt) '
313
+ 'and https://fosslight.org/fosslight-guide-en/scanner/3_dependency.html#-prerequisite.')
280
314
 
281
315
  if ret and graph_path:
282
316
  graph_path = os.path.abspath(graph_path)
@@ -335,6 +369,7 @@ def main():
335
369
  graph_path = ''
336
370
  graph_size = (600, 600)
337
371
  direct = True
372
+ recursive = False
338
373
 
339
374
  parser = argparse.ArgumentParser(add_help=False)
340
375
  parser.add_argument('-h', '--help', action='store_true', required=False)
@@ -353,6 +388,7 @@ def main():
353
388
  parser.add_argument('--graph-size', nargs=2, type=int, metavar=("WIDTH", "HEIGHT"), required=False)
354
389
  parser.add_argument('--direct', choices=('true', 'false'), default='True', required=False)
355
390
  parser.add_argument('--notice', action='store_true', required=False)
391
+ parser.add_argument('-r', '--recursive', action='store_true', required=False)
356
392
 
357
393
  args = parser.parse_args()
358
394
 
@@ -405,10 +441,12 @@ def main():
405
441
  paginate_file(source_file)
406
442
  shutil.copyfile(source_file, destination_file)
407
443
  sys.exit(0)
444
+ if args.recursive: # -r option
445
+ recursive = True
408
446
 
409
447
  run_dependency_scanner(package_manager, input_dir, output_dir, pip_activate_cmd, pip_deactivate_cmd,
410
448
  output_custom_dir, app_name, github_token, format, direct, path_to_exclude,
411
- graph_path, graph_size)
449
+ graph_path, graph_size, recursive)
412
450
 
413
451
 
414
452
  if __name__ == '__main__':
@@ -1,19 +1,22 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fosslight-dependency
3
- Version: 4.1.18
3
+ Version: 4.1.20
4
4
  Summary: FOSSLight Dependency Scanner
5
5
  Home-page: https://github.com/fosslight/fosslight_dependency_scanner
6
+ Download-URL: https://github.com/fosslight/fosslight_dependency_scanner
6
7
  Author: LG Electronics
7
8
  License: Apache-2.0
8
- Download-URL: https://github.com/fosslight/fosslight_dependency_scanner
9
- Platform: UNKNOWN
10
9
  Classifier: License :: OSI Approved :: Apache Software License
11
10
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.8
13
- Classifier: Programming Language :: Python :: 3.9
14
11
  Classifier: Programming Language :: Python :: 3.10
15
12
  Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.10,<3.13
16
15
  Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ License-File: LICENSES/Apache-2.0.txt
18
+ License-File: LICENSES/LicenseRef-3rd_party_licenses.txt
19
+ License-File: LICENSES/MIT.txt
17
20
  Requires-Dist: openpyxl
18
21
  Requires-Dist: beautifulsoup4
19
22
  Requires-Dist: lxml
@@ -190,5 +193,3 @@ Please see the [CONTRIBUTING guide](https://github.com/fosslight/fosslight_depen
190
193
 
191
194
  Copyright (c) 2020 LG Electronics, Inc.
192
195
  FOSSLight Dependency Scanner is licensed under Apache-2.0, as found in the [LICENSE](https://github.com/fosslight/fosslight_dependency_scanner/blob/main/LICENSE) file.
193
-
194
-
@@ -1,11 +1,11 @@
1
1
  fosslight_dependency/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- fosslight_dependency/_analyze_dependency.py,sha256=AKwKPEjA5x4p3oCWMJugQjd4zX9WKy9zfUqp44gL63Q,4617
2
+ fosslight_dependency/_analyze_dependency.py,sha256=oJ_6gka5V8cdafxoiQhgepGDnjO5EVM9JX4AdBacMzw,4707
3
3
  fosslight_dependency/_graph_convertor.py,sha256=D8GwmJfuj9Wg3_DeKRPLGGdyHSLcoU2Q0VzKQbkJG4g,2267
4
- fosslight_dependency/_help.py,sha256=DuvW8ltpT36VAXhs7yEFEnK8t7CHpG9v8uwrwykHXP8,3509
5
- fosslight_dependency/_package_manager.py,sha256=uwSrWTukE8WTtrcIra6iFw1qdI80vLjCuXStl3Da2-s,14916
4
+ fosslight_dependency/_help.py,sha256=wt7M6wnyJJr-RIQ1pvMceevpGAKCSTQsAlGiKOXMQUE,3595
5
+ fosslight_dependency/_package_manager.py,sha256=mN1ukEmZkm6COhxWm-mVfhCZkHppfFgXyXzBT1x02Sw,15016
6
6
  fosslight_dependency/constant.py,sha256=5upuTFSTmmQwqv2u-MRcEG4lxaIZAIi_c-rAPhOrsfM,1226
7
7
  fosslight_dependency/dependency_item.py,sha256=wNLWcsNycf3HQ5Pib2WrMeo2dn0eHCRg20NLcL95Qew,3345
8
- fosslight_dependency/run_dependency_scanner.py,sha256=0IO2-9Vi5Gkn7wURluencoQOMgCaSoEzup7UbPeATKM,18384
8
+ fosslight_dependency/run_dependency_scanner.py,sha256=1F9zrKd4VjeXI9l5M-ZW0qxq-Cf27u8u7_aw8l_O7vQ,20406
9
9
  fosslight_dependency/LICENSES/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
10
10
  fosslight_dependency/LICENSES/LicenseRef-3rd_party_licenses.txt,sha256=EcsFt7aE1rp3OXAdJgmXayfOZdpRdBMcmRnyoqWMCsw,95687
11
11
  fosslight_dependency/package_manager/Android.py,sha256=0UZFvbLxDIreerK4fR316YPyhUpPliV_kfZulrxkUyo,3218
@@ -16,20 +16,20 @@ fosslight_dependency/package_manager/Go.py,sha256=eEWvPoE3Jd0lMJAxWMNdFcoi21fJF0
16
16
  fosslight_dependency/package_manager/Gradle.py,sha256=IYmj9q3XiE_DPKdtll6lyRr98lFuyKWW2qz57X26Fn0,4359
17
17
  fosslight_dependency/package_manager/Helm.py,sha256=ucx2Y0tWX37UHIzIGaRyTe7uQ2vlu2nUuO09hOMq9ZU,4223
18
18
  fosslight_dependency/package_manager/Maven.py,sha256=vAiBEAEGQD5eaFGVSLmrZ8AAtoz_IuTmhaWuPTmsku0,10503
19
- fosslight_dependency/package_manager/Npm.py,sha256=W7mdJKyIfPlO_S3jPTZiESnBOg7vQZxpkHSPQDazmcE,10659
19
+ fosslight_dependency/package_manager/Npm.py,sha256=1ILIBYiU1gwi6yTbc4GSyjYf7omChoBntUBf_SnyBGU,10845
20
20
  fosslight_dependency/package_manager/Nuget.py,sha256=u4w084Qozk4nrVdT4o_nDiT8v4URIlXaOrDh11Hu1Bw,8885
21
21
  fosslight_dependency/package_manager/Pnpm.py,sha256=LDKooFGQHui_Q5U7XqSJ8KcCPiLVndXf5oGKTJExh5w,7056
22
22
  fosslight_dependency/package_manager/Pub.py,sha256=Rrz8_6wdrmMU6f3vbbuAwyMbODBauXNnBbI619OQgDk,10184
23
- fosslight_dependency/package_manager/Pypi.py,sha256=LPeI7amFXKxhXMSRwiVaKfhHL_D7IGUy5j0F9HBoVmE,16709
23
+ fosslight_dependency/package_manager/Pypi.py,sha256=Iko_MGEBPrYr1I2-430bG4mkAwFqTUPpjWWKfJ9SM1c,17178
24
24
  fosslight_dependency/package_manager/Swift.py,sha256=8fdbdAXTNlp2NDoSqQXm48JGAg9UhxA91M1-NhHkT40,6752
25
25
  fosslight_dependency/package_manager/Unity.py,sha256=n1006GZ6Qrk8wAdO6wla1Q-JD7Evin7REVj-HDeTARc,5142
26
26
  fosslight_dependency/package_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- fosslight_dependency-4.1.18.dist-info/Apache-2.0.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
- fosslight_dependency-4.1.18.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
- fosslight_dependency-4.1.18.dist-info/LicenseRef-3rd_party_licenses.txt,sha256=EcsFt7aE1rp3OXAdJgmXayfOZdpRdBMcmRnyoqWMCsw,95687
30
- fosslight_dependency-4.1.18.dist-info/METADATA,sha256=t7wrtXjUJqL3YH34cVWANBG25cj3c2kRD0sOF4P1aMw,5104
31
- fosslight_dependency-4.1.18.dist-info/MIT.txt,sha256=9cx4CbArgByWvkoEZNqpzbpJgA9TUe2D62rMocQpgfs,1082
32
- fosslight_dependency-4.1.18.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
33
- fosslight_dependency-4.1.18.dist-info/entry_points.txt,sha256=e1QZbnCrQvfbwe9L6PxXnkRZMhl-PSo0QyUes0dGjU8,91
34
- fosslight_dependency-4.1.18.dist-info/top_level.txt,sha256=Jc0V7VcVCH0TEM8ksb8dwroTYz4AmRaQnlr3FB71Hcs,21
35
- fosslight_dependency-4.1.18.dist-info/RECORD,,
27
+ fosslight_dependency-4.1.20.dist-info/Apache-2.0.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
+ fosslight_dependency-4.1.20.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
+ fosslight_dependency-4.1.20.dist-info/LicenseRef-3rd_party_licenses.txt,sha256=EcsFt7aE1rp3OXAdJgmXayfOZdpRdBMcmRnyoqWMCsw,95687
30
+ fosslight_dependency-4.1.20.dist-info/METADATA,sha256=9FAGK-KEqZOfJJUntY_zcvBL6Pjf-kTakBdTSrrZtEE,5213
31
+ fosslight_dependency-4.1.20.dist-info/MIT.txt,sha256=9cx4CbArgByWvkoEZNqpzbpJgA9TUe2D62rMocQpgfs,1082
32
+ fosslight_dependency-4.1.20.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
33
+ fosslight_dependency-4.1.20.dist-info/entry_points.txt,sha256=AeU-9Bl8al8Sa-XvhitGHdT3ZTPIrlhqADcp7s5OLF8,90
34
+ fosslight_dependency-4.1.20.dist-info/top_level.txt,sha256=Jc0V7VcVCH0TEM8ksb8dwroTYz4AmRaQnlr3FB71Hcs,21
35
+ fosslight_dependency-4.1.20.dist-info/RECORD,,
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  fosslight_dependency = fosslight_dependency.run_dependency_scanner:main
3
-