Skip to content

Commit 8da5dcf

Browse files
committed
Merge branch '396_alternative_android_module_license_output' into develop
2 parents 0ecf774 + dcaf5db commit 8da5dcf

File tree

10 files changed

+175
-8
lines changed

10 files changed

+175
-8
lines changed

REFERENCE.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ gen
151151

152152
::
153153

154+
--android Generate MODULE_LICENSE_XXX (XXX will be
155+
replaced by license key) and NOTICE as the same
156+
design as from Android.
157+
154158
--fetch-license api_url api_key Fetch licenses data from DejaCode License
155159
Library and create <license>.LICENSE
156160
side-by-side with the generated .ABOUT file.
@@ -178,6 +182,17 @@ Options
178182

179183
::
180184

185+
--android
186+
187+
Create an empty file named `MODULE_LICENSE_XXX` where `XXX` is the license
188+
key and create a NOTICE file which these two files follow the design from
189+
Android Open Source Project.
190+
191+
The input **must** have the license key information as this is needed to
192+
create the empty MODULE_LICENSE_XXX
193+
194+
$ about gen --android LOCATION OUTPUT
195+
181196
--fetch-license
182197

183198
Fetch licenses text from a DejaCode API. and create <license>.LICENSE side-by-side

src/attributecode/cmd.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf8 -*-
33

44
# ============================================================================
5-
# Copyright (c) 2013-2019 nexB Inc. http://www.nexb.com/ - All rights reserved.
5+
# Copyright (c) 2013-2020 nexB Inc. http://www.nexb.com/ - All rights reserved.
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
88
# You may obtain a copy of the License at
@@ -46,7 +46,7 @@
4646

4747

4848
__copyright__ = """
49-
Copyright (c) 2013-2019 nexB Inc and others. All rights reserved.
49+
Copyright (c) 2013-2020 nexB Inc and others. All rights reserved.
5050
Licensed under the Apache License, Version 2.0 (the "License");
5151
you may not use this file except in compliance with the License.
5252
You may obtain a copy of the License at
@@ -210,6 +210,11 @@ def inventory(location, output, format, quiet, verbose): # NOQA
210210
metavar='OUTPUT',
211211
type=click.Path(exists=True, file_okay=False, writable=True, resolve_path=True))
212212

213+
@click.option('--android',
214+
is_flag=True,
215+
help='Generate MODULE_LICENSE_XXX (XXX will be replaced by license key) and NOTICE '
216+
'as the same design as from Android.')
217+
213218
# FIXME: the CLI UX should be improved with two separate options for API key and URL
214219
@click.option('--fetch-license',
215220
nargs=2,
@@ -233,7 +238,7 @@ def inventory(location, output, format, quiet, verbose): # NOQA
233238

234239
@click.help_option('-h', '--help')
235240

236-
def gen(location, output, fetch_license, reference, quiet, verbose):
241+
def gen(location, output, android, fetch_license, reference, quiet, verbose):
237242
"""
238243
Generate .ABOUT files in OUTPUT from an inventory of .ABOUT files at LOCATION.
239244
@@ -245,12 +250,14 @@ def gen(location, output, fetch_license, reference, quiet, verbose):
245250
print_version()
246251
click.echo('Generating .ABOUT files...')
247252

253+
#FIXME: This should be checked in the `click`
248254
if not location.endswith(('.csv', '.json',)):
249255
raise click.UsageError('ERROR: Invalid input file extension: must be one .csv or .json.')
250256

251257
errors, abouts = generate_about_files(
252258
location=location,
253259
base_dir=output,
260+
android=android,
254261
reference_dir=reference,
255262
fetch_license=fetch_license,
256263
)

src/attributecode/gen.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,13 @@ def load_inventory(location, base_dir, reference_dir=None):
210210
def update_about_resource(self):
211211
pass
212212

213-
def generate(location, base_dir, reference_dir=None, fetch_license=False):
213+
def generate(location, base_dir, android=None, reference_dir=None, fetch_license=False):
214214
"""
215215
Load ABOUT data from a CSV inventory at `location`. Write ABOUT files to
216216
base_dir. Return errors and about objects.
217217
"""
218218
not_exist_errors = []
219+
notice_dict = {}
219220
api_url = ''
220221
api_key = ''
221222
gen_license = False
@@ -312,6 +313,21 @@ def generate(location, base_dir, reference_dir=None, fetch_license=False):
312313

313314
about.dump(dump_loc)
314315

316+
if android:
317+
"""
318+
Create MODULE_LICENSE_XXX and get context to create NOTICE file
319+
follow the standard from Android Open Source Project
320+
"""
321+
import os
322+
parent_path = os.path.dirname(util.to_posix(dump_loc))
323+
324+
about.android_module_license(parent_path)
325+
notice_path, notice_context = about.android_notice(parent_path)
326+
if notice_path in notice_dict.keys():
327+
notice_dict[notice_path] += '\n\n' + notice_context
328+
else:
329+
notice_dict[notice_path] = notice_context
330+
315331
for e in not_exist_errors:
316332
errors.append(Error(INFO, e))
317333

@@ -323,4 +339,14 @@ def generate(location, base_dir, reference_dir=None, fetch_license=False):
323339
u'%(dump_loc)s '
324340
u'with error: %(emsg)s' % locals())
325341
errors.append(Error(ERROR, msg))
342+
343+
if android:
344+
# Check if there is already a NOTICE file present
345+
for path in notice_dict.keys():
346+
if os.path.exists(path):
347+
msg = (u'NOTICE file already exist at: %s' % path)
348+
errors.append(Error(ERROR, msg))
349+
else:
350+
about.dump_android_notice(path, notice_dict[path])
351+
326352
return unique(errors), abouts

src/attributecode/model.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
# -*- coding: utf8 -*-
33
# ============================================================================
4-
# Copyright (c) 2013-2019 nexB Inc. http://www.nexb.com/ - All rights reserved.
4+
# Copyright (c) 2013-2020 nexB Inc. http://www.nexb.com/ - All rights reserved.
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
77
# You may obtain a copy of the License at
@@ -677,7 +677,7 @@ def validate_fields(fields, about_file_path, running_inventory, base_dir,
677677
def validate_field_name(name):
678678
if not is_valid_name(name):
679679
msg = ('Field name: %(name)r contains illegal name characters: '
680-
'0 to 9, a to z, A to Z and _.')
680+
'0 to 9, a to z, A to Z and _. (or empty spaces)')
681681
return Error(CRITICAL, msg % locals())
682682

683683

@@ -1095,6 +1095,53 @@ def dump(self, location):
10951095
dumped.write(genereated_tk_version)
10961096
dumped.write(self.dumps())
10971097

1098+
def dump_android_notice(self, path, context):
1099+
"""
1100+
Write the NOITCE file consist of copyright, notice and license
1101+
"""
1102+
if on_windows:
1103+
path = add_unc(path)
1104+
1105+
with io.open(path, mode='w', encoding='utf-8') as dumped:
1106+
dumped.write(context)
1107+
1108+
def android_module_license(self, about_parent_path):
1109+
"""
1110+
Create MODULE_LICENSE_XXX which the XXX is the value of license key.
1111+
"""
1112+
for lic_key in self.license_key.value:
1113+
# Make uppercase and with dash and spaces and dots replaced by underscore
1114+
# just to look similar and consistent.
1115+
name = 'MODULE_LICENSE_' + lic_key.replace('.', '_').replace('-', '_').replace(' ', '_').upper()
1116+
module_lic_path = os.path.join(about_parent_path, name)
1117+
# Create an empty MODULE_LICESE_XXX file
1118+
open(module_lic_path, 'a').close()
1119+
1120+
def android_notice(self, about_parent_path):
1121+
"""
1122+
Return a notice dictionary which the path of the notice file going
1123+
to create will be the key and its context will be the value of the dict.
1124+
"""
1125+
# Create NOTICE file with the combination context of copyright,
1126+
# notice_file and license_file
1127+
notice_path = posixpath.join(about_parent_path, 'NOTICE')
1128+
notice_context = ''
1129+
if self.copyright.value:
1130+
notice_context += self.copyright.value
1131+
if self.notice_file.value:
1132+
notice_file_dict = self.notice_file.value
1133+
notice_file_key = notice_file_dict.keys()
1134+
for key in notice_file_key:
1135+
if notice_file_dict[key]:
1136+
notice_context += '\n' + notice_file_dict[key] + '\n'
1137+
if self.license_file.value:
1138+
lic_file_dict = self.license_file.value
1139+
lic_file_key = lic_file_dict.keys()
1140+
for key in lic_file_key:
1141+
if lic_file_dict[key]:
1142+
notice_context += '\n\n' + lic_file_dict[key] + '\n\n'
1143+
return notice_path, notice_context
1144+
10981145
def dump_lic(self, location, license_dict):
10991146
"""
11001147
Write LICENSE files and return the a list of key, name, context and the url

tests/test_model.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf8 -*-
33

44
# ============================================================================
5-
# Copyright (c) 2014-2019 nexB Inc. http://www.nexb.com/ - All rights reserved.
5+
# Copyright (c) 2014-2020 nexB Inc. http://www.nexb.com/ - All rights reserved.
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
88
# You may obtain a copy of the License at
@@ -21,6 +21,7 @@
2121
from collections import OrderedDict
2222
import io
2323
import json
24+
import os
2425
import posixpath
2526
import shutil
2627
import unittest
@@ -40,6 +41,7 @@
4041
from attributecode.util import replace_tab_with_spaces
4142

4243
from testing_utils import extract_test_loc
44+
from testing_utils import get_temp_dir
4345
from testing_utils import get_temp_file
4446
from testing_utils import get_test_loc
4547

@@ -582,7 +584,7 @@ def test_About_rejects_non_ascii_names_and_accepts_unicode_values(self):
582584
test_file = get_test_loc('test_model/parse/non_ascii_field_name_value.about')
583585
a = model.About(test_file)
584586
expected = [
585-
Error(CRITICAL, "Field name: 'mat\xedas' contains illegal name characters: 0 to 9, a to z, A to Z and _.")
587+
Error(CRITICAL, "Field name: 'mat\xedas' contains illegal name characters: 0 to 9, a to z, A to Z and _. (or empty spaces)")
586588
]
587589
assert expected == a.errors
588590

@@ -971,6 +973,41 @@ def test_write_output_json(self):
971973
expected = get_test_loc('test_model/expected.json')
972974
check_json(expected, result)
973975

976+
def test_android_module_license(self):
977+
path = 'test_model/android/single_license.c.ABOUT'
978+
test_file = get_test_loc(path)
979+
abouts = model.About(location=test_file, about_file_path=path)
980+
981+
parent_dir = get_temp_dir()
982+
abouts.android_module_license(parent_dir)
983+
assert os.path.exists(os.path.join(parent_dir, 'MODULE_LICENSE_PUBLIC_DOMAIN'))
984+
985+
def test_android_module_multi_licenses(self):
986+
path = 'test_model/android/multi_license.c.ABOUT'
987+
test_file = get_test_loc(path)
988+
abouts = model.About(location=test_file, about_file_path=path)
989+
990+
parent_dir = get_temp_dir()
991+
abouts.android_module_license(parent_dir)
992+
assert os.path.exists(os.path.join(parent_dir, 'MODULE_LICENSE_BSD_NEW'))
993+
assert os.path.exists(os.path.join(parent_dir, 'MODULE_LICENSE_BSD_SIMPLIFIED'))
994+
995+
def test_android_notice(self):
996+
path = 'test_model/android/single_license.c.ABOUT'
997+
test_file = get_test_loc(path)
998+
abouts = model.About(location=test_file, about_file_path=path)
999+
1000+
parent_dir = get_temp_dir()
1001+
notice_path, notice_context = abouts.android_notice(parent_dir)
1002+
expected_path = os.path.join(parent_dir, 'NOTICE')
1003+
assert os.path.normpath(notice_path) == expected_path
1004+
1005+
expected_notice = '''Copyright (c) xyz
1006+
1007+
This component is released to the public domain by the author.
1008+
1009+
'''
1010+
assert notice_context == expected_notice
9741011

9751012
class CollectorTest(unittest.TestCase):
9761013

tests/testdata/test_cmd/help/about_gen_help.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Usage: about gen [OPTIONS] LOCATION OUTPUT
88
OUTPUT: Path to a directory where ABOUT files are generated.
99

1010
Options:
11+
--android Generate MODULE_LICENSE_XXX (XXX will be replaced by
12+
license key) and NOTICE as the same design as from
13+
Android.
1114
--fetch-license URL KEY Fetch license data and text files from a DejaCode
1215
License Library API URL using the API KEY.
1316
--reference DIR Path to a directory with reference license data and
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Copyright (c) xyz
2+
3+
This component is released to the public domain by the author.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated with AboutCode Toolkit Version 4.0.1
2+
3+
about_resource: multi_license.c
4+
name: multi_license.c
5+
license_expression: bsd-new OR bsd-simplified
6+
copyright: |
7+
Copyright (c) xyz
8+
Copyright (c) Blah blah BlAh
9+
licenses:
10+
- key: bsd-new
11+
name: BSD-3-Clause
12+
file: bsd-new.LICENSE
13+
url: https://enterprise.dejacode.com/urn/?urn=urn:dje:license:bsd-new
14+
- key: bsd-simplified
15+
name: BSD-2-Clause
16+
file: bsd-simplified.LICENSE
17+
url: https://enterprise.dejacode.com/urn/?urn=urn:dje:license:bsd-simplified
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This component is released to the public domain by the author.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Generated with AboutCode Toolkit Version 4.0.1
2+
3+
about_resource: single_license.c
4+
name: single_license.c
5+
license_expression: public-domain
6+
copyright: Copyright (c) xyz
7+
licenses:
8+
- key: public-domain
9+
name: Public Domain
10+
file: public-domain.LICENSE
11+
url: https://enterprise.dejacode.com/urn/?urn=urn:dje:license:public-domain

0 commit comments

Comments
 (0)