fosslight-source 2.3.8__tar.gz → 2.3.9__tar.gz
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.
- {fosslight_source-2.3.8/src/fosslight_source.egg-info → fosslight_source-2.3.9}/PKG-INFO +2 -2
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/pyproject.toml +2 -2
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_parsing_scancode_file_item.py +29 -3
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_scan_item.py +1 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/cli.py +11 -8
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/run_manifest_extractor.py +59 -28
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/run_scancode.py +2 -3
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/run_spdx_extractor.py +1 -1
- {fosslight_source-2.3.8 → fosslight_source-2.3.9/src/fosslight_source.egg-info}/PKG-INFO +2 -2
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/SOURCES.txt +3 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/requires.txt +1 -1
- fosslight_source-2.3.9/tests/test_manifest_android_bp.py +61 -0
- fosslight_source-2.3.9/tests/test_manifest_pyproject.py +51 -0
- fosslight_source-2.3.9/tests/test_manifest_recommended_scenarios.py +76 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/tests/test_parsing_unknown_spdx.py +63 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/LICENSE +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/MANIFEST.in +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/README.md +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/setup.cfg +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/__init__.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_help.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_kb_client.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_license_matched.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_merge.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_parsing_scanoss_file.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_scancode_ignore_binaries.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/run_scanoss.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/dependency_links.txt +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/entry_points.txt +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/top_level.txt +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/tests/test_manifest_composer.py +0 -0
- {fosslight_source-2.3.8 → fosslight_source-2.3.9}/tests/test_tox.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fosslight_source
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.9
|
|
4
4
|
Summary: FOSSLight Source Scanner
|
|
5
5
|
Author: LG Electronics
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -20,7 +20,7 @@ Requires-Dist: setuptools<=80.10.2
|
|
|
20
20
|
Requires-Dist: pyparsing
|
|
21
21
|
Requires-Dist: scanoss>=1.45.0
|
|
22
22
|
Requires-Dist: XlsxWriter
|
|
23
|
-
Requires-Dist: fosslight_util>=2.2.
|
|
23
|
+
Requires-Dist: fosslight_util>=2.2.8
|
|
24
24
|
Requires-Dist: PyYAML
|
|
25
25
|
Requires-Dist: wheel>=0.38.1
|
|
26
26
|
Requires-Dist: intbitset
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "fosslight_source"
|
|
10
|
-
version = "2.3.
|
|
10
|
+
version = "2.3.9"
|
|
11
11
|
description = "FOSSLight Source Scanner"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
license = "Apache-2.0"
|
|
@@ -29,7 +29,7 @@ dependencies = [
|
|
|
29
29
|
"pyparsing",
|
|
30
30
|
"scanoss>=1.45.0",
|
|
31
31
|
"XlsxWriter",
|
|
32
|
-
"fosslight_util>=2.2.
|
|
32
|
+
"fosslight_util>=2.2.8",
|
|
33
33
|
"PyYAML",
|
|
34
34
|
"wheel>=0.38.1",
|
|
35
35
|
"intbitset",
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import logging
|
|
8
8
|
import re
|
|
9
9
|
import fosslight_util.constant as constant
|
|
10
|
+
from fosslight_util.exclude import is_excluded_filename
|
|
10
11
|
from ._license_matched import MatchedLicense
|
|
11
12
|
from ._scan_item import SourceItem
|
|
12
13
|
from ._scan_item import replace_word
|
|
@@ -20,9 +21,15 @@ SPDX_LICENSE_IDENTIFIER_PATTERN = re.compile(
|
|
|
20
21
|
r'SPDX[-\s]+License[-\s]+Identifier(?:\s*[:,-]\s*|\s+)([^\r\n]+)',
|
|
21
22
|
re.IGNORECASE,
|
|
22
23
|
)
|
|
24
|
+
# Android Soong license_kinds string, e.g. "SPDX-license-identifier-BSD"
|
|
25
|
+
SOONG_SPDX_LICENSE_KIND_PATTERN = re.compile(
|
|
26
|
+
r'SPDX-license-identifier-([A-Za-z0-9.-]+)',
|
|
27
|
+
re.IGNORECASE,
|
|
28
|
+
)
|
|
23
29
|
LICENSE_REF_PREFIX_PATTERN = re.compile(r'^LicenseRef-', re.IGNORECASE)
|
|
24
30
|
# Trailing closers from comments and quoted lists, e.g. "MIT */", "MIT -->", or '"MIT",'.
|
|
25
31
|
SPDX_DECLARATION_TRAILER_PATTERN = re.compile(r'\s*(?:\*/|-->|["\']\s*,?)\s*$')
|
|
32
|
+
SPDX_DECLARATION_LINE_COMMENT_PATTERN = re.compile(r'\s//.*$')
|
|
26
33
|
KEYWORD_SPDX_ID = r'SPDX-License-Identifier\s*[\S]+'
|
|
27
34
|
KEYWORD_DOWNLOAD_LOC = r'DownloadLocation\s*[\S]+'
|
|
28
35
|
KEYWORD_SCANCODE_UNKNOWN = "unknown-spdx"
|
|
@@ -294,11 +301,22 @@ def _omit_parens_for_two_license_expression(expression: str) -> str:
|
|
|
294
301
|
|
|
295
302
|
|
|
296
303
|
def _clean_spdx_declaration(raw: str) -> str:
|
|
297
|
-
"""Strip whitespace and trailing comment or quoted-list terminators."""
|
|
304
|
+
"""Strip whitespace, line comments, and trailing comment or quoted-list terminators."""
|
|
298
305
|
cleaned = (raw or "").strip()
|
|
299
306
|
if not cleaned:
|
|
300
307
|
return ""
|
|
301
|
-
|
|
308
|
+
cleaned = SPDX_DECLARATION_LINE_COMMENT_PATTERN.sub('', cleaned).strip()
|
|
309
|
+
cleaned = SPDX_DECLARATION_TRAILER_PATTERN.sub('', cleaned).strip()
|
|
310
|
+
cleaned = cleaned.strip('"\'')
|
|
311
|
+
return cleaned.strip()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _extract_soong_license_kind(matched_txt: str) -> str:
|
|
315
|
+
"""Extract license id from Android Soong SPDX-license-identifier-* strings."""
|
|
316
|
+
matched = SOONG_SPDX_LICENSE_KIND_PATTERN.search(matched_txt or "")
|
|
317
|
+
if not matched:
|
|
318
|
+
return ""
|
|
319
|
+
return matched.group(1)
|
|
302
320
|
|
|
303
321
|
|
|
304
322
|
def _strip_license_ref_prefix(token: str) -> str:
|
|
@@ -309,9 +327,14 @@ def _extract_spdx_declared_expression(matched_txt: str) -> str:
|
|
|
309
327
|
"""
|
|
310
328
|
Extract SPDX-License-Identifier value from matched_text.
|
|
311
329
|
|
|
312
|
-
-
|
|
330
|
+
- Android Soong license_kinds (SPDX-license-identifier-*) first
|
|
331
|
+
- Removes trailing line comments and comment closers (*/, -->)
|
|
313
332
|
- Strips LicenseRef- from each AND/OR token
|
|
314
333
|
"""
|
|
334
|
+
soong_license = _extract_soong_license_kind(matched_txt)
|
|
335
|
+
if soong_license:
|
|
336
|
+
return soong_license
|
|
337
|
+
|
|
315
338
|
matched = SPDX_LICENSE_IDENTIFIER_PATTERN.search(matched_txt or "")
|
|
316
339
|
if not matched:
|
|
317
340
|
return ""
|
|
@@ -493,6 +516,9 @@ def parsing_scancode(
|
|
|
493
516
|
if (not file_path) or is_binary or is_dir:
|
|
494
517
|
logger.info(f"Skipping {file_path} because it is binary or directory")
|
|
495
518
|
continue
|
|
519
|
+
if is_excluded_filename(file_path):
|
|
520
|
+
logger.debug(f"Skipping {file_path} because it is an excluded filename")
|
|
521
|
+
continue
|
|
496
522
|
result_item = SourceItem(file_path)
|
|
497
523
|
if has_error:
|
|
498
524
|
error_msg = file.get("scan_errors", [])
|
|
@@ -29,13 +29,12 @@ import tqdm
|
|
|
29
29
|
import argparse
|
|
30
30
|
from .run_spdx_extractor import get_spdx_downloads
|
|
31
31
|
from .run_manifest_extractor import get_manifest_licenses
|
|
32
|
-
from ._scan_item import SourceItem, resolve_kb_config, is_notice_file
|
|
32
|
+
from ._scan_item import SourceItem, resolve_kb_config, is_notice_file, is_manifest_file
|
|
33
33
|
from ._kb_client import fetch_origin_urls_via_scan_job
|
|
34
34
|
from fosslight_util.cover import dump_result_log
|
|
35
35
|
from fosslight_util.time import current_timestamp_utc, format_running_time, timestamp_for_filename
|
|
36
36
|
from fosslight_util.oss_item import ScannerItem
|
|
37
37
|
from typing import Optional, Tuple
|
|
38
|
-
from ._scan_item import is_manifest_file
|
|
39
38
|
import shutil
|
|
40
39
|
from ._merge import (
|
|
41
40
|
_add_pre_merge_sheet,
|
|
@@ -454,11 +453,11 @@ def merge_results(
|
|
|
454
453
|
if manifest_licenses:
|
|
455
454
|
for file_name, licenses in manifest_licenses.items():
|
|
456
455
|
valid_licenses = [lic.strip() for lic in licenses if isinstance(lic, str) and lic.strip()]
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
456
|
+
item = _get_or_append_source_item(
|
|
457
|
+
scancode_result, file_name, append=bool(valid_licenses) or ui_mode
|
|
458
|
+
)
|
|
459
|
+
if item is None:
|
|
460
460
|
continue
|
|
461
|
-
item = _get_or_append_source_item(scancode_result, file_name)
|
|
462
461
|
item.is_manifest_file = True
|
|
463
462
|
if valid_licenses:
|
|
464
463
|
# overwrite existing detected licenses with manifest-provided licenses
|
|
@@ -501,9 +500,13 @@ def merge_results(
|
|
|
501
500
|
return scancode_result, kb_status_message, kb_requested_count, kb_returned_count
|
|
502
501
|
|
|
503
502
|
|
|
504
|
-
def _get_or_append_source_item(
|
|
503
|
+
def _get_or_append_source_item(
|
|
504
|
+
scancode_result: list, file_name: str, append: bool = True
|
|
505
|
+
) -> Optional[SourceItem]:
|
|
505
506
|
if file_name in scancode_result:
|
|
506
507
|
return scancode_result[scancode_result.index(file_name)]
|
|
508
|
+
if not append:
|
|
509
|
+
return None
|
|
507
510
|
item = SourceItem(file_name)
|
|
508
511
|
scancode_result.append(item)
|
|
509
512
|
return item
|
|
@@ -706,7 +709,7 @@ def run_scanners(
|
|
|
706
709
|
return success, result_log.get(RESULT_KEY, ""), scan_item, license_list, scanoss_result
|
|
707
710
|
|
|
708
711
|
|
|
709
|
-
def metadata_collector(path_to_scan: str, excluded_files: set) -> dict:
|
|
712
|
+
def metadata_collector(path_to_scan: str, excluded_files: set) -> tuple[dict, dict]:
|
|
710
713
|
"""
|
|
711
714
|
Collect metadata for merging.
|
|
712
715
|
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/run_manifest_extractor.py
RENAMED
|
@@ -178,16 +178,28 @@ def get_licenses_from_pyproject_toml(file_path: str) -> list[str]:
|
|
|
178
178
|
data = None
|
|
179
179
|
|
|
180
180
|
if isinstance(data, dict):
|
|
181
|
-
project_tbl = data.get('project')
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
181
|
+
project_tbl = data.get('project')
|
|
182
|
+
if isinstance(project_tbl, dict) and 'license' in project_tbl:
|
|
183
|
+
license_value = project_tbl.get('license')
|
|
184
|
+
if isinstance(license_value, str) and license_value.strip():
|
|
185
|
+
return [license_value.strip()]
|
|
186
|
+
if isinstance(license_value, dict):
|
|
187
|
+
text_value = license_value.get('text')
|
|
188
|
+
if isinstance(text_value, str) and text_value.strip():
|
|
189
|
+
return [text_value.strip()]
|
|
190
|
+
if license_value.get('file'):
|
|
191
|
+
return []
|
|
192
|
+
return []
|
|
193
|
+
|
|
194
|
+
tool_tbl = data.get('tool')
|
|
195
|
+
if isinstance(tool_tbl, dict):
|
|
196
|
+
poetry_tbl = tool_tbl.get('poetry')
|
|
197
|
+
if isinstance(poetry_tbl, dict):
|
|
198
|
+
poetry_license = poetry_tbl.get('license')
|
|
199
|
+
if isinstance(poetry_license, str) and poetry_license.strip():
|
|
200
|
+
return [poetry_license.strip()]
|
|
201
|
+
# Structured parse succeeded; do not re-read/regex the same file.
|
|
202
|
+
return []
|
|
191
203
|
except Exception as ex:
|
|
192
204
|
logger.info(f"Failed to parse pyproject.toml via toml parser for {file_path}: {ex}")
|
|
193
205
|
|
|
@@ -195,24 +207,40 @@ def get_licenses_from_pyproject_toml(file_path: str) -> list[str]:
|
|
|
195
207
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
196
208
|
content = f.read()
|
|
197
209
|
project_match = re.search(r'^\s*\[project\]\s*(.*?)(?=^\s*\[|\Z)', content, flags=re.MULTILINE | re.DOTALL)
|
|
198
|
-
if
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
210
|
+
if project_match:
|
|
211
|
+
block = project_match.group(1)
|
|
212
|
+
project_license_declared = re.search(r'^\s*license\s*=', block, flags=re.MULTILINE)
|
|
213
|
+
if project_license_declared:
|
|
214
|
+
m = re.search(r'^\s*license\s*=\s*(?P<q>"""|\'\'\'|"|\')(?P<val>.*?)(?P=q)', block,
|
|
215
|
+
flags=re.MULTILINE | re.DOTALL)
|
|
216
|
+
if m:
|
|
217
|
+
val = m.group('val').strip()
|
|
218
|
+
if val:
|
|
219
|
+
return [val]
|
|
220
|
+
m2 = re.search(r'^\s*license\s*=\s*\{[^}]*?\btext\s*=\s*(?P<q>"""|\'\'\'|"|\')(?P<val>.*?)(?P=q)',
|
|
221
|
+
block, flags=re.MULTILINE | re.DOTALL)
|
|
222
|
+
if m2:
|
|
223
|
+
val = m2.group('val').strip()
|
|
224
|
+
if val:
|
|
225
|
+
return [val]
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
poetry_match = re.search(
|
|
229
|
+
r'^\s*\[tool\.poetry\]\s*(.*?)(?=^\s*\[|\Z)',
|
|
230
|
+
content,
|
|
231
|
+
flags=re.MULTILINE | re.DOTALL,
|
|
232
|
+
)
|
|
233
|
+
if poetry_match:
|
|
234
|
+
poetry_block = poetry_match.group(1)
|
|
235
|
+
poetry_license = re.search(
|
|
236
|
+
r'^\s*license\s*=\s*(?P<q>"""|\'\'\'|"|\')(?P<val>.*?)(?P=q)',
|
|
237
|
+
poetry_block,
|
|
238
|
+
flags=re.MULTILINE | re.DOTALL,
|
|
239
|
+
)
|
|
240
|
+
if poetry_license:
|
|
241
|
+
val = poetry_license.group('val').strip()
|
|
242
|
+
if val:
|
|
243
|
+
return [val]
|
|
216
244
|
except Exception as ex:
|
|
217
245
|
logger.info(f"Failed to parse pyproject.toml {file_path}: {ex}")
|
|
218
246
|
return []
|
|
@@ -345,6 +373,9 @@ def get_licenses_from_huggingface_metadata(file_path: str) -> list[str]:
|
|
|
345
373
|
|
|
346
374
|
|
|
347
375
|
def get_manifest_licenses(file_path: str) -> list[str]:
|
|
376
|
+
# Android.bp licenses come from ScanCode; manifest merge only sets is_manifest_file.
|
|
377
|
+
if os.path.basename(file_path).lower() == 'android.bp':
|
|
378
|
+
return []
|
|
348
379
|
if file_path.endswith('.pom'):
|
|
349
380
|
try:
|
|
350
381
|
pom_licenses = get_license_from_pom(group_id='', artifact_id='', version='', pom_path=file_path, check_parent=True)
|
|
@@ -19,7 +19,6 @@ from fosslight_util.output_format import check_output_formats_v2
|
|
|
19
19
|
from fosslight_util.exclude import (
|
|
20
20
|
EXCLUDE_DIRECTORY,
|
|
21
21
|
EXCLUDE_FILE_EXTENSION,
|
|
22
|
-
EXCLUDE_FILENAME,
|
|
23
22
|
PACKAGE_DIRECTORY,
|
|
24
23
|
)
|
|
25
24
|
|
|
@@ -166,14 +165,14 @@ def _default_scancode_ignore_patterns(
|
|
|
166
165
|
Directory names use path-based globs (e.g. **/tests/**) so they do not match
|
|
167
166
|
the scan root directory name itself.
|
|
168
167
|
Binary files are excluded separately via scancode --ignore-binaries.
|
|
168
|
+
EXCLUDE_FILENAME is not passed to ScanCode --ignore: matching many exact
|
|
169
|
+
names on a large tree is slow. Those files are dropped after parsing.
|
|
169
170
|
"""
|
|
170
171
|
patterns = {".*"}
|
|
171
172
|
for name in PACKAGE_DIRECTORY + EXCLUDE_DIRECTORY:
|
|
172
173
|
patterns.add(_directory_ignore_pattern(name))
|
|
173
174
|
for ext in EXCLUDE_FILE_EXTENSION:
|
|
174
175
|
patterns.add(f"*.{ext}")
|
|
175
|
-
for name in EXCLUDE_FILENAME:
|
|
176
|
-
patterns.add(name)
|
|
177
176
|
|
|
178
177
|
for pattern in path_to_exclude or []:
|
|
179
178
|
if os.path.isabs(pattern):
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/run_spdx_extractor.py
RENAMED
|
@@ -22,5 +22,5 @@ def get_spdx_downloads(file_path: str) -> list[str]:
|
|
|
22
22
|
for word in find_word.findall(mmap_obj):
|
|
23
23
|
results.append(word.decode('utf-8'))
|
|
24
24
|
except Exception as ex:
|
|
25
|
-
logger.
|
|
25
|
+
logger.debug(f"Failed to extract SPDX download location. {file_path}, {ex}")
|
|
26
26
|
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fosslight_source
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.9
|
|
4
4
|
Summary: FOSSLight Source Scanner
|
|
5
5
|
Author: LG Electronics
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -20,7 +20,7 @@ Requires-Dist: setuptools<=80.10.2
|
|
|
20
20
|
Requires-Dist: pyparsing
|
|
21
21
|
Requires-Dist: scanoss>=1.45.0
|
|
22
22
|
Requires-Dist: XlsxWriter
|
|
23
|
-
Requires-Dist: fosslight_util>=2.2.
|
|
23
|
+
Requires-Dist: fosslight_util>=2.2.8
|
|
24
24
|
Requires-Dist: PyYAML
|
|
25
25
|
Requires-Dist: wheel>=0.38.1
|
|
26
26
|
Requires-Dist: intbitset
|
|
@@ -22,6 +22,9 @@ src/fosslight_source.egg-info/dependency_links.txt
|
|
|
22
22
|
src/fosslight_source.egg-info/entry_points.txt
|
|
23
23
|
src/fosslight_source.egg-info/requires.txt
|
|
24
24
|
src/fosslight_source.egg-info/top_level.txt
|
|
25
|
+
tests/test_manifest_android_bp.py
|
|
25
26
|
tests/test_manifest_composer.py
|
|
27
|
+
tests/test_manifest_pyproject.py
|
|
28
|
+
tests/test_manifest_recommended_scenarios.py
|
|
26
29
|
tests/test_parsing_unknown_spdx.py
|
|
27
30
|
tests/test_tox.py
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Copyright (c) 2026 LG Electronics Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""Tests for Android.bp manifest handling."""
|
|
4
|
+
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
from fosslight_source._scan_item import (
|
|
8
|
+
SourceItem,
|
|
9
|
+
is_manifest_file,
|
|
10
|
+
)
|
|
11
|
+
from fosslight_source.cli import merge_results, metadata_collector
|
|
12
|
+
from fosslight_source.run_manifest_extractor import get_manifest_licenses
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_is_manifest_file_recognizes_android_bp():
|
|
16
|
+
assert is_manifest_file("/tmp/module/Android.bp") is True
|
|
17
|
+
assert is_manifest_file("/tmp/module/android.bp") is True
|
|
18
|
+
assert is_manifest_file("/tmp/module/package.json") is True
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_get_manifest_licenses_returns_empty_for_android_bp(tmp_path):
|
|
22
|
+
android_bp = tmp_path / "Android.bp"
|
|
23
|
+
android_bp.write_text('license { name: "test_license" }', encoding="utf-8")
|
|
24
|
+
assert get_manifest_licenses(str(android_bp)) == []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_metadata_collector_adds_android_bp_without_license_extraction(tmp_path):
|
|
28
|
+
android_bp = tmp_path / "Android.bp"
|
|
29
|
+
android_bp.write_text('license { name: "test_license" }', encoding="utf-8")
|
|
30
|
+
package_json = tmp_path / "package.json"
|
|
31
|
+
package_json.write_text('{"license": "MIT"}', encoding="utf-8")
|
|
32
|
+
|
|
33
|
+
with patch("fosslight_source.cli.get_manifest_licenses", wraps=get_manifest_licenses) as mock_get:
|
|
34
|
+
spdx_downloads, manifest_licenses = metadata_collector(str(tmp_path), set())
|
|
35
|
+
|
|
36
|
+
assert spdx_downloads == {}
|
|
37
|
+
assert manifest_licenses == {"Android.bp": [], "package.json": ["MIT"]}
|
|
38
|
+
assert mock_get.call_count == 2
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_merge_results_sets_manifest_flag_without_overwriting_scancode_licenses():
|
|
42
|
+
scancode_item = SourceItem("carrois-gothic-sc/Android.bp")
|
|
43
|
+
scancode_item.licenses = ["Apache-2.0", "MIT", "BSD"]
|
|
44
|
+
|
|
45
|
+
merged, _, _, _ = merge_results(
|
|
46
|
+
scancode_result=[scancode_item],
|
|
47
|
+
manifest_licenses={"carrois-gothic-sc/Android.bp": []},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
assert len(merged) == 1
|
|
51
|
+
assert merged[0].is_manifest_file is True
|
|
52
|
+
assert merged[0].licenses == ["Apache-2.0", "MIT", "BSD"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_merge_results_skips_android_bp_not_in_scancode_result():
|
|
56
|
+
merged, _, _, _ = merge_results(
|
|
57
|
+
scancode_result=[],
|
|
58
|
+
manifest_licenses={"module/Android.bp": []},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
assert merged == []
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Copyright (c) 2026 LG Electronics Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""Tests for pyproject.toml manifest license extraction."""
|
|
4
|
+
|
|
5
|
+
from fosslight_source.run_manifest_extractor import get_licenses_from_pyproject_toml
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_pep621_license_string(tmp_path):
|
|
9
|
+
path = tmp_path / "pyproject.toml"
|
|
10
|
+
path.write_text(
|
|
11
|
+
'[project]\nname = "demo"\nlicense = "MIT"\n',
|
|
12
|
+
encoding="utf-8",
|
|
13
|
+
)
|
|
14
|
+
assert get_licenses_from_pyproject_toml(str(path)) == ["MIT"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_pep621_license_text_table(tmp_path):
|
|
18
|
+
path = tmp_path / "pyproject.toml"
|
|
19
|
+
path.write_text(
|
|
20
|
+
'[project]\nname = "demo"\nlicense = {text = "MIT"}\n',
|
|
21
|
+
encoding="utf-8",
|
|
22
|
+
)
|
|
23
|
+
assert get_licenses_from_pyproject_toml(str(path)) == ["MIT"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_pep621_license_file_returns_empty(tmp_path):
|
|
27
|
+
path = tmp_path / "pyproject.toml"
|
|
28
|
+
path.write_text(
|
|
29
|
+
'[project]\nname = "demo"\nlicense = {file = "LICENSE"}\n',
|
|
30
|
+
encoding="utf-8",
|
|
31
|
+
)
|
|
32
|
+
assert get_licenses_from_pyproject_toml(str(path)) == []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_legacy_poetry_license(tmp_path):
|
|
36
|
+
path = tmp_path / "pyproject.toml"
|
|
37
|
+
path.write_text(
|
|
38
|
+
'[tool.poetry]\nname = "demo"\nlicense = "Apache-2.0"\n',
|
|
39
|
+
encoding="utf-8",
|
|
40
|
+
)
|
|
41
|
+
assert get_licenses_from_pyproject_toml(str(path)) == ["Apache-2.0"]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_project_license_file_does_not_fall_back_to_poetry(tmp_path):
|
|
45
|
+
path = tmp_path / "pyproject.toml"
|
|
46
|
+
path.write_text(
|
|
47
|
+
'[project]\nname = "demo"\nlicense = {file = "LICENSE"}\n\n'
|
|
48
|
+
'[tool.poetry]\nname = "demo"\nlicense = "MIT"\n',
|
|
49
|
+
encoding="utf-8",
|
|
50
|
+
)
|
|
51
|
+
assert get_licenses_from_pyproject_toml(str(path)) == []
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Copyright (c) 2026 LG Electronics Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""Recommended verification scenarios for Android.bp manifest handling."""
|
|
4
|
+
|
|
5
|
+
from fosslight_source._scan_item import SourceItem
|
|
6
|
+
from fosslight_source.cli import merge_results
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_scenario1_android_bp_keeps_scancode_licenses():
|
|
10
|
+
"""Android.bp with ScanCode licenses: is_manifest_file=True, licenses preserved."""
|
|
11
|
+
scancode_item = SourceItem("Android.bp")
|
|
12
|
+
scancode_item.licenses = ["Apache-2.0", "unknown-license-reference", "BSD", "MIT", "OFL"]
|
|
13
|
+
|
|
14
|
+
merged, _, _, _ = merge_results(
|
|
15
|
+
scancode_result=[scancode_item],
|
|
16
|
+
manifest_licenses={"Android.bp": []},
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
assert len(merged) == 1
|
|
20
|
+
assert merged[0].is_manifest_file is True
|
|
21
|
+
assert merged[0].licenses == ["Apache-2.0", "unknown-license-reference", "BSD", "MIT", "OFL"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_scenario2_package_json_manifest_fail_keeps_scancode_licenses():
|
|
25
|
+
"""package.json manifest extraction fails but ScanCode row exists: manifest flag only."""
|
|
26
|
+
scancode_item = SourceItem("app/package.json")
|
|
27
|
+
scancode_item.licenses = ["Apache-2.0"]
|
|
28
|
+
|
|
29
|
+
merged, _, _, _ = merge_results(
|
|
30
|
+
scancode_result=[scancode_item],
|
|
31
|
+
manifest_licenses={"app/package.json": []},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
assert len(merged) == 1
|
|
35
|
+
assert merged[0].is_manifest_file is True
|
|
36
|
+
assert merged[0].licenses == ["Apache-2.0"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_scenario3_android_bp_from_spdx_marks_manifest_without_new_row():
|
|
40
|
+
"""Android.bp added by spdx merge: manifest flag on existing item, no duplicate row."""
|
|
41
|
+
spdx_item = SourceItem("module/Android.bp")
|
|
42
|
+
spdx_item.download_location = ["https://example.com/repo"]
|
|
43
|
+
|
|
44
|
+
merged, _, _, _ = merge_results(
|
|
45
|
+
scancode_result=[spdx_item],
|
|
46
|
+
manifest_licenses={"module/Android.bp": []},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
assert len(merged) == 1
|
|
50
|
+
assert merged[0].is_manifest_file is True
|
|
51
|
+
assert merged[0].download_location == ["https://example.com/repo"]
|
|
52
|
+
assert merged[0].licenses == []
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_scenario3_android_bp_not_in_result_no_row_non_ui():
|
|
56
|
+
"""Android.bp absent from merge result: no row in non-UI mode."""
|
|
57
|
+
merged_non_ui, _, _, _ = merge_results(
|
|
58
|
+
scancode_result=[],
|
|
59
|
+
manifest_licenses={"module/Android.bp": []},
|
|
60
|
+
ui_mode=False,
|
|
61
|
+
)
|
|
62
|
+
assert merged_non_ui == []
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_scenario3_android_bp_not_in_result_ui_keeps_empty_row():
|
|
66
|
+
"""Android.bp absent from merge result: UI mode still creates manifest row."""
|
|
67
|
+
merged_ui, _, _, _ = merge_results(
|
|
68
|
+
scancode_result=[],
|
|
69
|
+
manifest_licenses={"module/Android.bp": []},
|
|
70
|
+
ui_mode=True,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert len(merged_ui) == 1
|
|
74
|
+
assert merged_ui[0].source_name_or_path == "module/Android.bp"
|
|
75
|
+
assert merged_ui[0].is_manifest_file is True
|
|
76
|
+
assert merged_ui[0].licenses == []
|
|
@@ -21,6 +21,7 @@ from fosslight_source._parsing_scancode_file_item import (
|
|
|
21
21
|
("// SPDX-License-Identifier, MIT", "MIT"),
|
|
22
22
|
("// SPDX-License-Identifier MIT", "MIT"),
|
|
23
23
|
(' "SPDX-license-identifier-BSD",', "BSD"),
|
|
24
|
+
(' "SPDX-license-identifier-OFL", // by exception only', "OFL"),
|
|
24
25
|
("/* SPDX-License-Identifier: MIT */", "MIT"),
|
|
25
26
|
("<!-- SPDX-License-Identifier: MIT -->", "MIT"),
|
|
26
27
|
("# SPDX-License-Identifier: LicenseRef-MIT-like", "MIT-like"),
|
|
@@ -281,3 +282,65 @@ def test_two_license_comment_omits_outer_parentheses():
|
|
|
281
282
|
assert success is True
|
|
282
283
|
assert results[0].licenses == ["Apache-2.0", "MIT"]
|
|
283
284
|
assert results[0].comment == "Apache-2.0 OR MIT"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def test_android_bp_soong_license_kinds_without_line_comment_in_license():
|
|
288
|
+
scancode_file_list = [{
|
|
289
|
+
"path": "Android.bp",
|
|
290
|
+
"type": "file",
|
|
291
|
+
"detected_license_expression": (
|
|
292
|
+
"(apache-2.0 AND unknown-license-reference) AND (unknown-spdx AND mit)"
|
|
293
|
+
),
|
|
294
|
+
"license_detections": [
|
|
295
|
+
{
|
|
296
|
+
"license_expression": "apache-2.0 AND unknown-license-reference",
|
|
297
|
+
"matches": [
|
|
298
|
+
{
|
|
299
|
+
"license_expression": "apache-2.0",
|
|
300
|
+
"matched_text": (
|
|
301
|
+
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
302
|
+
"// limitations under the License."
|
|
303
|
+
),
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"license_expression": "unknown-license-reference",
|
|
307
|
+
"matched_text": "// *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE",
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"license_expression": "unknown-spdx AND mit",
|
|
313
|
+
"matches": [
|
|
314
|
+
{
|
|
315
|
+
"license_expression": "unknown-spdx",
|
|
316
|
+
"matched_text": ' "SPDX-license-identifier-BSD",',
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"license_expression": "mit",
|
|
320
|
+
"matched_text": ' "SPDX-license-identifier-MIT",',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"license_expression": "unknown-spdx",
|
|
324
|
+
"matched_text": (
|
|
325
|
+
' "SPDX-license-identifier-OFL", // by exception only'
|
|
326
|
+
),
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
"copyrights": [],
|
|
332
|
+
}]
|
|
333
|
+
|
|
334
|
+
success, results, _messages, _ = parsing_scancode(scancode_file_list)
|
|
335
|
+
|
|
336
|
+
assert success is True
|
|
337
|
+
licenses = results[0].licenses
|
|
338
|
+
assert licenses == [
|
|
339
|
+
"Apache-2.0",
|
|
340
|
+
"unknown-license-reference",
|
|
341
|
+
"BSD",
|
|
342
|
+
"MIT",
|
|
343
|
+
"OFL",
|
|
344
|
+
]
|
|
345
|
+
assert all("//" not in lic for lic in results[0].licenses)
|
|
346
|
+
assert all('"' not in lic for lic in results[0].licenses)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_parsing_scanoss_file.py
RENAMED
|
File without changes
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source/_scancode_ignore_binaries.py
RENAMED
|
File without changes
|
|
File without changes
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{fosslight_source-2.3.8 → fosslight_source-2.3.9}/src/fosslight_source.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|