Skip to content

Commit 2fabd4c

Browse files
xclaessejpakkane
authored andcommitted
minstall: Add --skip-subprojects option
By default all subprojects are installed. If --skip-subprojects is given with no value only the main project is installed. If --skip-subprojects is given with a value, it should be a coma separated list of subprojects to skip and all others will be installed. Fixes: #2550.
1 parent 36d9d7a commit 2fabd4c

File tree

21 files changed

+195
-77
lines changed

21 files changed

+195
-77
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## Skip subprojects installation
2+
3+
It is now possible to skip installation of some or all subprojects. This is
4+
useful when subprojects are internal dependencies static linked into the main
5+
project.
6+
7+
By default all subprojects are still installed.
8+
- `meson install -C builddir --skip-subprojects` installs only the main project.
9+
- `meson install -C builddir --skip-subprojects foo,bar` installs the main project
10+
and all subprojects except for subprojects `foo` and `bar` if they are used.

mesonbuild/backend/backends.py

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import re
2424
import typing as T
2525
import hashlib
26-
import copy
2726

2827
from .. import build
2928
from .. import dependencies
@@ -100,20 +99,18 @@ def __init__(self, source_dir: str, build_dir: str, prefix: str,
10099
self.strip_bin = strip_bin
101100
self.install_umask = install_umask
102101
self.targets: T.List[TargetInstallData] = []
103-
self.headers: 'InstallType' = []
104-
self.man: 'InstallType' = []
105-
self.data: 'InstallType' = []
106-
self.po_package_name: str = ''
107-
self.po = []
102+
self.headers: T.List[InstallDataBase] = []
103+
self.man: T.List[InstallDataBase] = []
104+
self.data: T.List[InstallDataBase] = []
108105
self.install_scripts: T.List[ExecutableSerialisation] = []
109-
self.install_subdirs: 'InstallSubdirsType' = []
106+
self.install_subdirs: T.List[SubdirInstallData] = []
110107
self.mesonintrospect = mesonintrospect
111108
self.version = version
112109

113110
class TargetInstallData:
114111
def __init__(self, fname: str, outdir: str, aliases: T.Dict[str, str], strip: bool,
115112
install_name_mappings: T.Dict, rpath_dirs_to_remove: T.Set[bytes],
116-
install_rpath: str, install_mode: 'FileMode', optional: bool = False):
113+
install_rpath: str, install_mode: 'FileMode', subproject: str, optional: bool = False):
117114
self.fname = fname
118115
self.outdir = outdir
119116
self.aliases = aliases
@@ -122,8 +119,21 @@ def __init__(self, fname: str, outdir: str, aliases: T.Dict[str, str], strip: bo
122119
self.rpath_dirs_to_remove = rpath_dirs_to_remove
123120
self.install_rpath = install_rpath
124121
self.install_mode = install_mode
122+
self.subproject = subproject
125123
self.optional = optional
126124

125+
class InstallDataBase:
126+
def __init__(self, path: str, install_path: str, install_mode: 'FileMode', subproject: str):
127+
self.path = path
128+
self.install_path = install_path
129+
self.install_mode = install_mode
130+
self.subproject = subproject
131+
132+
class SubdirInstallData(InstallDataBase):
133+
def __init__(self, path: str, install_path: str, install_mode: 'FileMode', exclude, subproject: str):
134+
super().__init__(path, install_path, install_mode, subproject)
135+
self.exclude = exclude
136+
127137
class ExecutableSerialisation:
128138
def __init__(self, cmd_args, env: T.Optional[build.EnvironmentVariables] = None, exe_wrapper=None,
129139
workdir=None, extra_paths=None, capture=None) -> None:
@@ -138,6 +148,7 @@ def __init__(self, cmd_args, env: T.Optional[build.EnvironmentVariables] = None,
138148
self.pickled = False
139149
self.skip_if_destdir = False
140150
self.verbose = False
151+
self.subproject = ''
141152

142153
class TestSerialisation:
143154
def __init__(self, name: str, project: str, suite: str, fname: T.List[str],
@@ -972,7 +983,7 @@ def generate_depmf_install(self, d: InstallData) -> None:
972983
with open(ifilename, 'w') as f:
973984
f.write(json.dumps(mfobj))
974985
# Copy file from, to, and with mode unchanged
975-
d.data.append((ifilename, ofilename, None))
986+
d.data.append(InstallDataBase(ifilename, ofilename, None, ''))
976987

977988
def get_regen_filelist(self):
978989
'''List of all files whose alteration means that the build
@@ -1297,7 +1308,7 @@ def generate_target_install(self, d: InstallData) -> None:
12971308
i = TargetInstallData(self.get_target_filename(t), outdirs[0],
12981309
t.get_aliases(), should_strip, mappings,
12991310
t.rpath_dirs_to_remove,
1300-
t.install_rpath, install_mode)
1311+
t.install_rpath, install_mode, t.subproject)
13011312
d.targets.append(i)
13021313

13031314
if isinstance(t, (build.SharedLibrary, build.SharedModule, build.Executable)):
@@ -1315,14 +1326,15 @@ def generate_target_install(self, d: InstallData) -> None:
13151326
# Install the import library; may not exist for shared modules
13161327
i = TargetInstallData(self.get_target_filename_for_linking(t),
13171328
implib_install_dir, {}, False, {}, set(), '', install_mode,
1318-
optional=isinstance(t, build.SharedModule))
1329+
t.subproject, optional=isinstance(t, build.SharedModule))
13191330
d.targets.append(i)
13201331

13211332
if not should_strip and t.get_debug_filename():
13221333
debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename())
13231334
i = TargetInstallData(debug_file, outdirs[0],
13241335
{}, False, {}, set(), '',
1325-
install_mode, optional=True)
1336+
install_mode, t.subproject,
1337+
optional=True)
13261338
d.targets.append(i)
13271339
# Install secondary outputs. Only used for Vala right now.
13281340
if num_outdirs > 1:
@@ -1331,7 +1343,8 @@ def generate_target_install(self, d: InstallData) -> None:
13311343
if outdir is False:
13321344
continue
13331345
f = os.path.join(self.get_target_dir(t), output)
1334-
i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode)
1346+
i = TargetInstallData(f, outdir, {}, False, {}, set(), None,
1347+
install_mode, t.subproject)
13351348
d.targets.append(i)
13361349
elif isinstance(t, build.CustomTarget):
13371350
# If only one install_dir is specified, assume that all
@@ -1345,7 +1358,7 @@ def generate_target_install(self, d: InstallData) -> None:
13451358
for output in t.get_outputs():
13461359
f = os.path.join(self.get_target_dir(t), output)
13471360
i = TargetInstallData(f, outdirs[0], {}, False, {}, set(), None, install_mode,
1348-
optional=not t.build_by_default)
1361+
t.subproject, optional=not t.build_by_default)
13491362
d.targets.append(i)
13501363
else:
13511364
for output, outdir in zip(t.get_outputs(), outdirs):
@@ -1354,23 +1367,11 @@ def generate_target_install(self, d: InstallData) -> None:
13541367
continue
13551368
f = os.path.join(self.get_target_dir(t), output)
13561369
i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode,
1357-
optional=not t.build_by_default)
1370+
t.subproject, optional=not t.build_by_default)
13581371
d.targets.append(i)
13591372

13601373
def generate_custom_install_script(self, d: InstallData) -> None:
1361-
result: T.List[ExecutableSerialisation] = []
1362-
srcdir = self.environment.get_source_dir()
1363-
builddir = self.environment.get_build_dir()
1364-
for i in self.build.install_scripts:
1365-
fixed_args = []
1366-
for a in i.cmd_args:
1367-
a = a.replace('@SOURCE_ROOT@', srcdir)
1368-
a = a.replace('@BUILD_ROOT@', builddir)
1369-
fixed_args.append(a)
1370-
es = copy.copy(i)
1371-
es.cmd_args = fixed_args
1372-
result.append(es)
1373-
d.install_scripts = result
1374+
d.install_scripts = self.build.install_scripts
13741375

13751376
def generate_header_install(self, d: InstallData) -> None:
13761377
incroot = self.environment.get_includedir()
@@ -1387,7 +1388,7 @@ def generate_header_install(self, d: InstallData) -> None:
13871388
msg = 'Invalid header type {!r} can\'t be installed'
13881389
raise MesonException(msg.format(f))
13891390
abspath = f.absolute_path(srcdir, builddir)
1390-
i = (abspath, outdir, h.get_custom_install_mode())
1391+
i = InstallDataBase(abspath, outdir, h.get_custom_install_mode(), h.subproject)
13911392
d.headers.append(i)
13921393

13931394
def generate_man_install(self, d: InstallData) -> None:
@@ -1401,7 +1402,7 @@ def generate_man_install(self, d: InstallData) -> None:
14011402
subdir = os.path.join(manroot, 'man' + num)
14021403
srcabs = f.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
14031404
dstabs = os.path.join(subdir, os.path.basename(f.fname))
1404-
i = (srcabs, dstabs, m.get_custom_install_mode())
1405+
i = InstallDataBase(srcabs, dstabs, m.get_custom_install_mode(), m.subproject)
14051406
d.man.append(i)
14061407

14071408
def generate_data_install(self, d: InstallData):
@@ -1416,7 +1417,7 @@ def generate_data_install(self, d: InstallData):
14161417
for src_file, dst_name in zip(de.sources, de.rename):
14171418
assert(isinstance(src_file, mesonlib.File))
14181419
dst_abs = os.path.join(subdir, dst_name)
1419-
i = (src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode)
1420+
i = InstallDataBase(src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode, de.subproject)
14201421
d.data.append(i)
14211422

14221423
def generate_subdir_install(self, d: InstallData) -> None:
@@ -1432,8 +1433,8 @@ def generate_subdir_install(self, d: InstallData) -> None:
14321433
sd.install_dir)
14331434
if not sd.strip_directory:
14341435
dst_dir = os.path.join(dst_dir, os.path.basename(src_dir))
1435-
d.install_subdirs.append(
1436-
(src_dir, dst_dir, sd.install_mode, sd.exclude))
1436+
i = SubdirInstallData(src_dir, dst_dir, sd.install_mode, sd.exclude, sd.subproject)
1437+
d.install_subdirs.append(i)
14371438

14381439
def get_introspection_data(self, target_id: str, target: build.Target) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
14391440
'''

mesonbuild/build.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,13 @@ def __init__(self, dep, node, explicit=True):
127127
class Headers:
128128

129129
def __init__(self, sources: T.List[File], install_subdir: T.Optional[str],
130-
install_dir: T.Optional[str], install_mode: T.Optional['FileMode']):
130+
install_dir: T.Optional[str], install_mode: T.Optional['FileMode'],
131+
subproject: str):
131132
self.sources = sources
132133
self.install_subdir = install_subdir
133134
self.custom_install_dir = install_dir
134135
self.custom_install_mode = install_mode
136+
self.subproject = subproject
135137

136138
# TODO: we really don't need any of these methods, but they're preserved to
137139
# keep APIs relying on them working.
@@ -155,10 +157,11 @@ def get_custom_install_mode(self) -> T.Optional['FileMode']:
155157
class Man:
156158

157159
def __init__(self, sources: T.List[File], install_dir: T.Optional[str],
158-
install_mode: T.Optional['FileMode']):
160+
install_mode: T.Optional['FileMode'], subproject: str):
159161
self.sources = sources
160162
self.custom_install_dir = install_dir
161163
self.custom_install_mode = install_mode
164+
self.subproject = subproject
162165

163166
def get_custom_install_dir(self) -> T.Optional[str]:
164167
return self.custom_install_dir
@@ -175,14 +178,16 @@ class InstallDir:
175178
def __init__(self, src_subdir: str, inst_subdir: str, install_dir: str,
176179
install_mode: T.Optional['FileMode'],
177180
exclude: T.Tuple[T.Set[str], T.Set[str]],
178-
strip_directory: bool, from_source_dir: bool = True):
181+
strip_directory: bool, subproject: str,
182+
from_source_dir: bool = True):
179183
self.source_subdir = src_subdir
180184
self.installable_subdir = inst_subdir
181185
self.install_dir = install_dir
182186
self.install_mode = install_mode
183187
self.exclude = exclude
184188
self.strip_directory = strip_directory
185189
self.from_source_dir = from_source_dir
190+
self.subproject = subproject
186191

187192

188193
class Build:
@@ -2616,14 +2621,16 @@ def keys(self) -> T.Iterator[str]:
26162621
# during install.
26172622
class Data:
26182623
def __init__(self, sources: T.List[File], install_dir: str,
2619-
install_mode: T.Optional['FileMode'] = None, rename: T.List[str] = None):
2624+
install_mode: T.Optional['FileMode'], subproject: str,
2625+
rename: T.List[str] = None):
26202626
self.sources = sources
26212627
self.install_dir = install_dir
26222628
self.install_mode = install_mode
26232629
if rename is None:
26242630
self.rename = [os.path.basename(f.fname) for f in self.sources]
26252631
else:
26262632
self.rename = rename
2633+
self.subproject = subproject
26272634

26282635
class TestSetup:
26292636
def __init__(self, exe_wrapper: T.Optional[T.List[str]], gdb: bool,

mesonbuild/interpreter.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,7 +1966,9 @@ def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder
19661966
else:
19671967
m = 'Script or command {!r} not found or not executable'
19681968
raise InterpreterException(m.format(prog))
1969-
return self.interpreter.backend.get_executable_serialisation([found] + args)
1969+
es = self.interpreter.backend.get_executable_serialisation([found] + args)
1970+
es.subproject = self.interpreter.subproject
1971+
return es
19701972

19711973
def _process_script_args(
19721974
self, name: str, args: T.List[T.Union[
@@ -2584,6 +2586,7 @@ def process_new_values(self, invalues):
25842586
elif isinstance(v, build.GeneratedList):
25852587
pass
25862588
elif isinstance(v, ExecutableSerialisation):
2589+
v.subproject = self.subproject
25872590
self.build.install_scripts.append(v)
25882591
elif isinstance(v, build.Data):
25892592
self.build.data.append(v)
@@ -2593,8 +2596,9 @@ def process_new_values(self, invalues):
25932596
# FIXME: This is special cased and not ideal:
25942597
# The first source is our new VapiTarget, the rest are deps
25952598
self.process_new_values(v.sources[0])
2596-
elif isinstance(v, InstallDirHolder):
2597-
self.build.install_dirs.append(v.held_object)
2599+
elif isinstance(v, build.InstallDir):
2600+
self.build.install_dirs.append(v)
2601+
return InstallDirHolder(v)
25982602
elif isinstance(v, Test):
25992603
self.build.tests.append(v)
26002604
elif hasattr(v, 'held_object'):
@@ -4229,7 +4233,7 @@ def func_install_headers(self, node, args, kwargs):
42294233
if install_dir is not None and not isinstance(install_dir, str):
42304234
raise InterpreterException('install_dir keyword argument must be a string if provided')
42314235

4232-
h = build.Headers(source_files, install_subdir, install_dir, install_mode)
4236+
h = build.Headers(source_files, install_subdir, install_dir, install_mode, self.subproject)
42334237
self.build.headers.append(h)
42344238

42354239
return HeadersHolder(h)
@@ -4250,7 +4254,7 @@ def func_install_man(self, node, args, kwargs):
42504254
if custom_install_dir is not None and not isinstance(custom_install_dir, str):
42514255
raise InterpreterException('install_dir must be a string.')
42524256

4253-
m = build.Man(sources, custom_install_dir, custom_install_mode)
4257+
m = build.Man(sources, custom_install_dir, custom_install_mode, self.subproject)
42544258
self.build.man.append(m)
42554259

42564260
return ManHolder(m)
@@ -4350,7 +4354,7 @@ def func_install_data(self, node, args: T.List, kwargs: T.Dict[str, T.Any]):
43504354
'"rename" and "sources" argument lists must be the same length if "rename" is given. '
43514355
f'Rename has {len(rename)} elements and sources has {len(sources)}.')
43524356

4353-
data = DataHolder(build.Data(sources, install_dir, install_mode, rename))
4357+
data = DataHolder(build.Data(sources, install_dir, install_mode, self.subproject, rename))
43544358
self.build.data.append(data.held_object)
43554359
return data
43564360

@@ -4397,7 +4401,7 @@ def func_install_subdir(self, node, args, kwargs):
43974401
exclude_directories = set()
43984402
exclude = (exclude_files, exclude_directories)
43994403
install_mode = self._get_kwarg_install_mode(kwargs)
4400-
idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory)
4404+
idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory, self.subproject)
44014405
self.build.install_dirs.append(idir)
44024406
return InstallDirHolder(idir)
44034407

@@ -4587,7 +4591,7 @@ def func_configure_file(self, node, args, kwargs):
45874591
'is true')
45884592
cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname)
45894593
install_mode = self._get_kwarg_install_mode(kwargs)
4590-
self.build.data.append(build.Data([cfile], idir, install_mode))
4594+
self.build.data.append(build.Data([cfile], idir, install_mode, self.subproject))
45914595
return mesonlib.File.from_built_file(self.subdir, output)
45924596

45934597
def extract_incdirs(self, kwargs):

0 commit comments

Comments
 (0)