Skip to content

Commit 5067be7

Browse files
roxellbhcopeland
authored andcommitted
tuxmake: accept Kbuild targets as positional arguments
Building a single file or subdir needs dropping into the container and running make manually. Accept positional names that contain / or end in .o or .ko. Pass them to make: tuxmake drivers/mmc/ tuxmake config kernel/livepatch/patch.o Add a --make-target flag for the same: tuxmake --make-target=drivers/mmc/ Bareword typos still fail with UnsupportedTarget. Works with --toolchain and --runtime like other flags. Signed-off-by: Anders Roxell <anders.roxell@linaro.org>
1 parent 3975415 commit 5067be7

8 files changed

Lines changed: 134 additions & 5 deletions

File tree

test/test_build.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,49 @@ def test_unsupported_target(self, linux):
9898
build(tree=linux, targets=["unknown-target"])
9999

100100

101+
class TestMakeTarget:
102+
def test_adds_make_target(self, linux):
103+
b = Build(tree=linux, targets=[], make_target=["drivers/mmc/"])
104+
names = [t.name for t in b.targets]
105+
assert "make:drivers/mmc/" in names
106+
107+
def test_pulls_in_config(self, linux):
108+
b = Build(tree=linux, targets=[], make_target=["drivers/mmc/"])
109+
names = [t.name for t in b.targets]
110+
assert "config" in names
111+
112+
def test_does_not_pull_defaults(self, linux):
113+
b = Build(tree=linux, targets=[], make_target=["drivers/mmc/"])
114+
names = [t.name for t in b.targets]
115+
assert "default" not in names
116+
assert "kernel" not in names
117+
118+
def test_positional_make_target(self, linux):
119+
b = Build(tree=linux, targets=["config", "drivers/dma/"])
120+
names = [t.name for t in b.targets]
121+
assert "make:drivers/dma/" in names
122+
assert "config" in names
123+
124+
def test_positional_object_target(self, linux):
125+
b = Build(tree=linux, targets=["config", "kernel/livepatch/patch.o"])
126+
names = [t.name for t in b.targets]
127+
assert "make:kernel/livepatch/patch.o" in names
128+
129+
def test_unknown_bareword_still_fails(self, linux):
130+
with pytest.raises(tuxmake.exceptions.UnsupportedTarget):
131+
Build(tree=linux, targets=["typo-target"])
132+
133+
def test_multiple_make_targets(self, linux):
134+
b = Build(
135+
tree=linux,
136+
targets=[],
137+
make_target=["drivers/mmc/", "kernel/livepatch/patch.o"],
138+
)
139+
names = [t.name for t in b.targets]
140+
assert "make:drivers/mmc/" in names
141+
assert "make:kernel/livepatch/patch.o" in names
142+
143+
101144
class TestKconfig:
102145
def test_kconfig_default(self, linux, Popen):
103146
b = Build(tree=linux, targets=["config"])

test/test_cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ def test_config_multiple(self, builder):
7070
tuxmake("config", "kernel")
7171
assert args(builder).targets == ["config", "kernel"]
7272

73+
def test_make_target(self, builder):
74+
tuxmake("--make-target=drivers/mmc/")
75+
assert args(builder).make_target == ["drivers/mmc/"]
76+
77+
def test_make_target_forces_empty_targets(self, builder):
78+
tuxmake("--make-target=drivers/mmc/")
79+
assert args(builder).targets == []
80+
81+
def test_make_target_with_positional(self, builder):
82+
tuxmake("config", "--make-target=drivers/mmc/")
83+
assert args(builder).targets == ["config"]
84+
assert args(builder).make_target == ["drivers/mmc/"]
85+
7386

7487
class TestMakeVariables:
7588
def test_basic(self, builder):

test/test_cmdline.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ def test_kconfig_add(self, cmdline):
6161
assert "--kconfig-add=foo.config" in cmd
6262
assert "--kconfig-add=bar.config" in cmd
6363

64+
def test_make_target(self, cmdline):
65+
build = Build(targets=[], make_target=["drivers/mmc/"])
66+
cmd = cmdline.reproduce(build)
67+
assert "--make-target=drivers/mmc/" in cmd
68+
6469
def test_debug(self, cmdline):
6570
cmd = cmdline.reproduce(Build(debug=True))
6671
assert "--debug" in cmd

test/test_target.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22

33
import tuxmake.exceptions
44
from tuxmake.arch import Native
5-
from tuxmake.target import Compression, Target, Config, Kselftest, create_target
5+
from tuxmake.target import (
6+
Compression,
7+
Target,
8+
Config,
9+
Kselftest,
10+
MakeTarget,
11+
create_target,
12+
)
613

714

815
@pytest.fixture
@@ -231,6 +238,21 @@ def test_kdir_set_to_build_dir(self, build):
231238
assert kselftest.makevars.get("KDIR") == "{build_dir}"
232239

233240

241+
class TestMakeTarget:
242+
@pytest.fixture
243+
def make_target(self, build):
244+
return MakeTarget("drivers/mmc/", build)
245+
246+
def test_name(self, make_target):
247+
assert make_target.name == "make:drivers/mmc/"
248+
249+
def test_commands(self, make_target):
250+
assert make_target.commands[0] == ["{make}", "drivers/mmc/"]
251+
252+
def test_depends_on_config(self, make_target):
253+
assert make_target.dependencies == ["config"]
254+
255+
234256
class TestCompression:
235257
def test_invalid_compression(self):
236258
with pytest.raises(tuxmake.exceptions.UnsupportedCompression):

tuxmake/build.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from tuxmake.output import get_new_output_dir, get_default_korg_toolchains_dir
1919
from tuxmake.target import Compression
2020
from tuxmake.target import default_compression
21+
from tuxmake.target import MakeTarget
2122
from tuxmake.target import create_target
2223
from tuxmake.runtime import Runtime, DockerRuntime
2324
from tuxmake.runtime import Terminated
@@ -181,6 +182,7 @@ def __init__(
181182
targets=defaults.targets,
182183
compression_type=None,
183184
kernel_image=None,
185+
make_target=None,
184186
jobs=None,
185187
runtime=None,
186188
fail_fast=False,
@@ -227,7 +229,7 @@ def __init__(
227229
self.dynamic_make_variables = dict(self.target_arch.dynamic_makevars)
228230

229231
if not targets:
230-
targets = defaults.targets
232+
targets = [] if make_target else defaults.targets
231233

232234
if kernel_image:
233235
self.target_overrides = {"kernel": kernel_image}
@@ -242,6 +244,13 @@ def __init__(
242244
self.__ordering_only_targets__ = {}
243245
for t in targets:
244246
self.add_target(t)
247+
self.make_target = make_target or []
248+
if self.make_target:
249+
self.add_target("config")
250+
for mt in self.make_target:
251+
target = MakeTarget(mt, self, self.compression)
252+
self.__ordering_only_targets__[target.name] = False
253+
self.targets.append(target)
245254
self.cleanup_targets()
246255
self.extend_kconfig()
247256

@@ -301,10 +310,10 @@ def add_target(self, target_name, ordering_only=False):
301310
target = create_target(target_name, self, self.compression)
302311

303312
if ordering_only:
304-
if target_name not in self.__ordering_only_targets__:
305-
self.__ordering_only_targets__[target_name] = True
313+
if target.name not in self.__ordering_only_targets__:
314+
self.__ordering_only_targets__[target.name] = True
306315
else:
307-
self.__ordering_only_targets__[target_name] = False
316+
self.__ordering_only_targets__[target.name] = False
308317

309318
for d in target.dependencies:
310319
self.add_target(d, ordering_only=ordering_only)

tuxmake/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ def format_yes_no(b, length):
159159
"results_hooks",
160160
]
161161
}
162+
if options.make_target and not options.targets:
163+
build_args["targets"] = []
162164
try:
163165
if options.download_all_korg_gcc_toolchains:
164166
with tempfile.TemporaryDirectory() as _tmpdir:

tuxmake/cmdline.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ def build_parser(cls=argparse.ArgumentParser, **kwargs):
104104
type=str,
105105
help="Kernel image to build, overriding the default image name for the target architecture.",
106106
)
107+
target.add_argument(
108+
"-M",
109+
"--make-target",
110+
type=str,
111+
action="append",
112+
default=[],
113+
help="Arbitrary Kbuild target to pass to make, e.g. drivers/mmc/ or kernel/livepatch/patch.o. Can be specified multiple times. Kbuild-like positional targets (with / or .o/.ko extensions) also work without this flag.",
114+
)
107115

108116
buildenv = parser.add_argument_group("Build environment options")
109117
buildenv.add_argument(

tuxmake/target.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import List, Tuple
22

3+
from configparser import ConfigParser
34
from pathlib import Path
45
import re
56
import shlex
@@ -279,6 +280,23 @@ def __init_config__(self):
279280
self.artifacts["vmlinux"] = "vmlinux"
280281

281282

283+
class MakeTarget(Target):
284+
"""Pass-through target that runs `make <make_target>`."""
285+
286+
def __init__(self, make_target, build, compression=default_compression):
287+
self.build = build
288+
self.compression = compression
289+
self.target_arch = build.target_arch
290+
self.name = f"make:{make_target}"
291+
self.config = ConfigParser()
292+
self.config["target"] = {
293+
"description": f"make {make_target}",
294+
"dependencies": "config",
295+
"commands": shlex.quote("{make}") + " " + shlex.quote(make_target),
296+
}
297+
self.__init_config__()
298+
299+
282300
class Kselftest(Target):
283301
def __init_config__(self):
284302
super().__init_config__()
@@ -303,6 +321,15 @@ def _is_clang_toolchain(self):
303321
}
304322

305323

324+
_make_target_re = re.compile(r"/|\.(o|ko|s|i|lst|dtb|dtbo)$")
325+
326+
306327
def create_target(name, build, compression=default_compression):
307328
cls = __special_targets__.get(name, Target)
329+
# Fall back to a pass-through make target when the name looks like
330+
# a Kbuild target (contains / or ends in .o / .ko / .s / .i / ...).
331+
# Lets users pass drivers/dma/ or kernel/livepatch/patch.o as
332+
# positional arguments without --make-target=.
333+
if cls is Target and _make_target_re.search(name):
334+
return MakeTarget(name, build, compression)
308335
return cls(name, build, compression)

0 commit comments

Comments
 (0)