Skip to content

Commit 0473956

Browse files
authored
Merge pull request #1799 from nexB/1678-bazel-parser
Parse BUILD files Signed-off-by: Philippe Ombredanne <[email protected]>
2 parents 92102c8 + 606e270 commit 0473956

File tree

25 files changed

+183
-314
lines changed

25 files changed

+183
-314
lines changed

src/packagedcode/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
from packagedcode import pypi
4242
from packagedcode import rpm
4343
from packagedcode import rubygems
44-
from packagedcode import buck
4544

4645

4746
# Note: the order matters: from the most to the least specific
@@ -85,7 +84,7 @@
8584
models.SquashfsPackage,
8685
chef.ChefPackage,
8786
build.BazelPackage,
88-
buck.BuckPackage,
87+
build.BuckPackage,
8988
build.AutotoolsPackage,
9089
conda.CondaPackage,
9190
]

src/packagedcode/buck.py

Lines changed: 0 additions & 209 deletions
This file was deleted.

src/packagedcode/build.py

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,19 @@
2626
from __future__ import print_function
2727
from __future__ import unicode_literals
2828

29+
from collections import defaultdict
30+
from collections import OrderedDict
31+
import ast
2932
import logging
33+
import os
3034

3135
import attr
3236

3337
from commoncode import filetype
3438
from commoncode import fileutils
3539
from packagedcode import models
40+
from packagedcode.utils import combine_expressions
41+
from scancode.api import get_licenses
3642

3743

3844
TRACE = False
@@ -91,7 +97,94 @@ class AutotoolsPackage(BaseBuildManifestPackage):
9197
default_type = 'autotools'
9298

9399

100+
starlark_rule_types = [
101+
'binary',
102+
'library'
103+
]
104+
105+
106+
@attr.s()
107+
class StarlarkManifestPackage(BaseBuildManifestPackage):
108+
@classmethod
109+
def recognize(cls, location):
110+
if not cls._is_build_manifest(location):
111+
return
112+
113+
# Thanks to Starlark being a Python dialect, we can use the `ast`
114+
# library to parse it
115+
with open(location, 'rb') as f:
116+
tree = ast.parse(f.read())
117+
118+
build_rules = defaultdict(list)
119+
for statement in tree.body:
120+
# We only care about function calls or assignments to functions whose
121+
# names ends with one of the strings in `rule_types`
122+
if (isinstance(statement, ast.Expr)
123+
or isinstance(statement, ast.Call)
124+
or isinstance(statement, ast.Assign)
125+
and isinstance(statement.value, ast.Call)
126+
and isinstance(statement.value.func, ast.Name)
127+
and statement.value.func.id.endswith(starlark_rule_types)):
128+
rule_name = statement.value.func.id
129+
# Process the rule arguments
130+
args = OrderedDict()
131+
for kw in statement.value.keywords:
132+
arg_name = kw.arg
133+
if isinstance(kw.value, ast.Str):
134+
args[arg_name] = kw.value.s
135+
if isinstance(kw.value, ast.List):
136+
# We collect the elements of a list if the element is not a function call
137+
args[arg_name] = [elt.s for elt in kw.value.elts if not isinstance(elt, ast.Call)]
138+
if args:
139+
build_rules[rule_name].append(args)
140+
141+
if build_rules:
142+
for rule_name, rule_instances_args in build_rules.items():
143+
for args in rule_instances_args:
144+
name = args.get('name')
145+
if not name:
146+
continue
147+
license_files = args.get('licenses')
148+
yield cls(
149+
name=name,
150+
declared_license=license_files,
151+
root_path=fileutils.parent_directory(location)
152+
)
153+
else:
154+
# If we don't find anything in the manifest file, we yield a Package with
155+
# the parent directory as the name
156+
yield cls(
157+
name=fileutils.file_name(fileutils.parent_directory(location))
158+
)
159+
160+
def compute_normalized_license(self):
161+
"""
162+
Return a normalized license expression string detected from a list of
163+
declared license items.
164+
"""
165+
declared_license = self.declared_license
166+
manifest_parent_path = self.root_path
167+
168+
if not declared_license or not manifest_parent_path:
169+
return
170+
171+
license_expressions = []
172+
for license_file in declared_license:
173+
license_file_path = os.path.join(manifest_parent_path, license_file)
174+
if os.path.exists(license_file_path) and os.path.isfile(license_file_path):
175+
licenses = get_licenses(license_file_path)
176+
license_expressions.extend(licenses.get('license_expressions', []))
177+
178+
return combine_expressions(license_expressions)
179+
180+
94181
@attr.s()
95-
class BazelPackage(BaseBuildManifestPackage):
182+
class BazelPackage(StarlarkManifestPackage):
96183
metafiles = ('BUILD',)
97184
default_type = 'bazel'
185+
186+
187+
@attr.s()
188+
class BuckPackage(StarlarkManifestPackage):
189+
metafiles = ('BUCK',)
190+
default_type = 'buck'

0 commit comments

Comments
 (0)