• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2024 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Bazel Tests."""
15
16import pathlib
17import unittest
18import xml.etree.ElementTree
19
20from pw_build_mcuxpresso.bazel import (
21    BazelVariable,
22    generate_bazel_targets,
23    headers_cc_library,
24    import_targets,
25    target_to_bazel,
26)
27from pw_build_mcuxpresso.common import BuildTarget
28from pw_build_mcuxpresso.components import Project
29
30# pylint: disable=missing-function-docstring
31# pylint: disable=line-too-long
32
33
34def setup_test_project() -> Project:
35    test_manifest_xml = '''
36    <manifest>
37      <components>
38        <component id="test">
39          <dependencies>
40            <component_dependency value="foo"/>
41            <component_dependency value="bar"/>
42          </dependencies>
43        </component>
44        <component id="foo" package_base_path="foo">
45          <defines>
46            <define name="FOO"/>
47          </defines>
48          <source relative_path="include" type="c_include">
49            <files mask="foo.h"/>
50          </source>
51          <source relative_path="src" type="src">
52            <files mask="foo.cc"/>
53          </source>
54          <include_paths>
55            <include_path relative_path="include" type="c_include"/>
56          </include_paths>
57        </component>
58        <component id="bar" package_base_path="bar">
59          <defines>
60            <define name="BAR"/>
61          </defines>
62          <source relative_path="include" type="c_include">
63            <files mask="bar.h"/>
64          </source>
65          <source relative_path="src" type="src">
66            <files mask="bar.cc"/>
67          </source>
68          <include_paths>
69            <include_path relative_path="include" type="c_include"/>
70          </include_paths>
71        </component>
72        <component id="frodo" package_base_path="frodo">
73          <dependencies>
74            <component_dependency value="bilbo"/>
75            <component_dependency value="smeagol"/>
76          </dependencies>
77          <defines>
78            <define name="FRODO"/>
79          </defines>
80          <source relative_path="include" type="c_include">
81            <files mask="frodo.h"/>
82          </source>
83          <source relative_path="src" type="src">
84            <files mask="frodo.cc"/>
85          </source>
86          <source toolchain="armgcc" relative_path="./" type="lib">
87            <files mask="libonering.a"/>
88          </source>
89          <include_paths>
90            <include_path relative_path="include" type="c_include"/>
91          </include_paths>
92        </component>
93        <component id="bilbo" package_base_path="bilbo">
94          <defines>
95            <define name="BILBO"/>
96          </defines>
97          <source relative_path="include" type="c_include">
98            <files mask="bilbo.h"/>
99          </source>
100          <source relative_path="src" type="src">
101            <files mask="bilbo.cc"/>
102          </source>
103          <include_paths>
104            <include_path relative_path="include" type="c_include"/>
105          </include_paths>
106        </component>
107        <component id="smeagol" package_base_path="smeagol">
108          <dependencies>
109            <component_dependency value="gollum"/>
110          </dependencies>
111          <source toolchain="armgcc" relative_path="./" type="lib">
112            <files mask="libonering.a"/>
113          </source>
114        </component>
115        <component id="gollum" package_base_path="gollum">
116          <dependencies>
117            <component_dependency value="smeagol"/>
118          </dependencies>
119          <source toolchain="armgcc" relative_path="./" type="lib">
120            <files mask="libonering.a"/>
121          </source>
122        </component>
123      </components>
124    </manifest>
125    '''
126    manifest = (
127        xml.etree.ElementTree.fromstring(test_manifest_xml),
128        pathlib.Path.cwd() / "manifest.xml",
129    )
130    return Project([manifest], pathlib.Path.cwd(), ["test", "frodo"])
131
132
133class TargetToBazelTest(unittest.TestCase):
134    """target_to_bazel tests."""
135
136    def test_str_attrib(self):
137        expected_format = r'''
138test_rule(
139    name = "foo",
140    bar = "baz",
141)
142'''.strip()
143
144        target = BuildTarget("test_rule", "foo", {"bar": "baz"})
145
146        self.assertEqual(target_to_bazel(target), expected_format)
147
148    def test_num_attr(self):
149        expected_format = r'''
150test_rule(
151    name = "foo",
152    bar = 13,
153)
154'''.strip()
155
156        target = BuildTarget(
157            "test_rule",
158            "foo",
159            {
160                "bar": 13,
161            },
162        )
163
164        self.assertEqual(target_to_bazel(target), expected_format)
165
166    def test_bool_attr(self):
167        expected_format = r'''
168test_rule(
169    name = "foo",
170    bar = True,
171    baz = False,
172)
173'''.strip()
174
175        target = BuildTarget(
176            "test_rule",
177            "foo",
178            {
179                "bar": True,
180                "baz": False,
181            },
182        )
183
184        self.assertEqual(target_to_bazel(target), expected_format)
185
186    def test_target_attr(self):
187        expected_format = r'''
188test_rule(
189    name = "foo",
190    bar = ":baz",
191)
192'''.strip()
193
194        target = BuildTarget(
195            "test_rule",
196            "foo",
197            {"bar": BuildTarget("test_rule", "baz")},
198        )
199
200        self.assertEqual(target_to_bazel(target), expected_format)
201
202    def test_bazel_var_attr(self):
203        expected_format = r'''
204test_rule(
205    name = "foo",
206    bar = MY_VAR,
207)
208'''.strip()
209
210        target = BuildTarget(
211            "test_rule",
212            "foo",
213            {"bar": BazelVariable("MY_VAR", "")},
214        )
215
216        self.assertEqual(target_to_bazel(target), expected_format)
217
218    def test_list_attr(self):
219        expected_format = r'''
220test_rule(
221    name = "foo",
222    bar = ['baz', 13, True, ':quox'],
223)
224'''.strip()
225
226        target = BuildTarget(
227            "test_rule",
228            "foo",
229            {
230                "bar": [
231                    "baz",
232                    13,
233                    True,
234                    BuildTarget("test_rule", "quox"),
235                ],
236            },
237        )
238
239        self.assertEqual(target_to_bazel(target), expected_format)
240
241    def test_full_rule(self):
242        expected_format = r'''
243lore(
244    name = "gollum",
245    age = 600,
246    is_hobbit = True,
247    needs = [':fish', ':ring'],
248    other_name = "smeagol",
249)
250'''.strip()
251
252        target = BuildTarget(
253            "lore",
254            "gollum",
255            {
256                "other_name": "smeagol",
257                "age": 600,
258                "is_hobbit": True,
259                "needs": [
260                    BuildTarget("test_rule", "fish"),
261                    BuildTarget("test_rule", "ring"),
262                ],
263            },
264        )
265
266        self.assertEqual(target_to_bazel(target), expected_format)
267
268    def test_indent(self):
269        expected_format = r'''
270test_rule(
271  name = "foo",
272  indent = 2,
273)
274'''.strip()
275
276        target = BuildTarget("test_rule", "foo", {"indent": 2})
277
278        self.assertEqual(target_to_bazel(target, 2), expected_format)
279
280
281class ImportTargetsTest(unittest.TestCase):
282    """import_targets tests."""
283
284    def test_single_lib(self):
285        library = [pathlib.Path("testlib.a")]
286
287        targets = import_targets(library)
288
289        self.assertEqual(len(targets), 1)
290        self.assertListEqual(
291            targets,
292            [
293                BuildTarget(
294                    "cc_import",
295                    "testlib.a",
296                    {
297                        "static_library": "testlib.a",
298                    },
299                )
300            ],
301        )
302
303    def test_nested_lib(self):
304        library = [pathlib.Path("some/path/to/testlib.a")]
305
306        targets = import_targets(library)
307
308        self.assertEqual(len(targets), 1)
309        self.assertListEqual(
310            targets,
311            [
312                BuildTarget(
313                    "cc_import",
314                    "some.path.to.testlib.a",
315                    {
316                        "static_library": "some/path/to/testlib.a",
317                    },
318                )
319            ],
320        )
321
322    def test_multi_libs(self):
323        libraries = [
324            pathlib.Path("some/path/to/testlib.a"),
325            pathlib.Path("testlib.a"),
326            pathlib.Path("libonering.a"),
327        ]
328
329        targets = import_targets(libraries)
330
331        self.assertEqual(len(targets), len(libraries))
332        self.assertListEqual(
333            targets,
334            [
335                BuildTarget(
336                    "cc_import",
337                    "libonering.a",
338                    {
339                        "static_library": "libonering.a",
340                    },
341                ),
342                BuildTarget(
343                    "cc_import",
344                    "some.path.to.testlib.a",
345                    {
346                        "static_library": "some/path/to/testlib.a",
347                    },
348                ),
349                BuildTarget(
350                    "cc_import",
351                    "testlib.a",
352                    {
353                        "static_library": "testlib.a",
354                    },
355                ),
356            ],
357        )
358
359
360class HeadersCommonTest(unittest.TestCase):
361    """headers_cc_library tests."""
362
363    def test_common_component(self):
364        expected_format = '''
365cc_library(
366    name = "commons",
367    copts = COPTS,
368    defines = ['BAR', 'BILBO', 'FOO', 'FRODO'],
369    deps = [':user_config'],
370    hdrs = ['bar/include/bar.h', 'bilbo/include/bilbo.h', 'foo/include/foo.h', 'frodo/include/frodo.h'],
371    includes = ['bar/include', 'bilbo/include', 'foo/include', 'frodo/include'],
372)
373'''.strip()
374
375        project = setup_test_project()
376
377        commons = headers_cc_library(project)
378
379        self.assertIsNotNone(commons.attrs.get("defines"))
380        self.assertListEqual(
381            commons.attrs["defines"], ["BAR", "BILBO", "FOO", "FRODO"]
382        )
383
384        self.assertIsNotNone(commons.attrs.get("hdrs"))
385        self.assertListEqual(
386            commons.attrs["hdrs"],
387            [
388                'bar/include/bar.h',
389                'bilbo/include/bilbo.h',
390                'foo/include/foo.h',
391                'frodo/include/frodo.h',
392            ],
393        )
394
395        self.assertIsNotNone(commons.attrs.get("includes"))
396        self.assertListEqual(
397            commons.attrs["includes"],
398            ['bar/include', 'bilbo/include', 'foo/include', 'frodo/include'],
399        )
400
401        self.assertEqual(target_to_bazel(commons), expected_format)
402
403
404class ProjectTargetsTest(unittest.TestCase):
405    """generate_project_targets tests."""
406
407    def test_project(self):
408        expected_format = '''
409label_flag(
410    name = "user_config",
411    build_setting_default = "@pigweed//pw_build:empty_cc_library",
412)
413cc_library(
414    name = "commons",
415    copts = COPTS,
416    defines = ['BAR', 'BILBO', 'FOO', 'FRODO'],
417    deps = [':user_config'],
418    hdrs = ['bar/include/bar.h', 'bilbo/include/bilbo.h', 'foo/include/foo.h', 'frodo/include/frodo.h'],
419    includes = ['bar/include', 'bilbo/include', 'foo/include', 'frodo/include'],
420)
421cc_import(
422    name = "frodo.libonering.a",
423    static_library = "frodo/libonering.a",
424)
425cc_import(
426    name = "gollum.libonering.a",
427    static_library = "gollum/libonering.a",
428)
429cc_import(
430    name = "smeagol.libonering.a",
431    static_library = "smeagol/libonering.a",
432)
433cc_library(
434    name = "bar",
435    copts = COPTS,
436    deps = [':commons'],
437    srcs = ['bar/src/bar.cc'],
438)
439cc_library(
440    name = "bilbo",
441    copts = COPTS,
442    deps = [':commons'],
443    srcs = ['bilbo/src/bilbo.cc'],
444)
445cc_library(
446    name = "foo",
447    copts = COPTS,
448    deps = [':commons'],
449    srcs = ['foo/src/foo.cc'],
450)
451cc_library(
452    name = "frodo",
453    copts = COPTS,
454    deps = [':bilbo', ':commons', ':frodo.libonering.a', ':smeagol'],
455    srcs = ['frodo/src/frodo.cc'],
456)
457cc_library(
458    name = "gollum",
459    copts = COPTS,
460    deps = [':commons', ':gollum.libonering.a', ':smeagol.libonering.a'],
461)
462cc_library(
463    name = "smeagol",
464    copts = COPTS,
465    deps = [':commons', ':gollum.libonering.a', ':smeagol.libonering.a'],
466)
467cc_library(
468    name = "test",
469    copts = COPTS,
470    deps = [':bar', ':commons', ':foo'],
471)
472'''.strip()
473
474        project = setup_test_project()
475
476        targets = list(generate_bazel_targets(project))
477        self.assertEqual(len(targets), 12)
478
479        targets = "\n".join(target_to_bazel(target) for target in targets)
480        self.assertEqual(targets, expected_format)
481
482
483if __name__ == '__main__':
484    unittest.main()
485