aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/syntax-highlighting/vim/syntax/meson.vim1
-rw-r--r--docs/markdown/snippets/install_emptydir.md18
-rw-r--r--docs/yaml/functions/install_emptydir.yaml27
-rw-r--r--docs/yaml/functions/install_subdir.yaml9
-rw-r--r--mesonbuild/ast/interpreter.py1
-rw-r--r--mesonbuild/backend/backends.py15
-rw-r--r--mesonbuild/build.py14
-rw-r--r--mesonbuild/interpreter/interpreter.py13
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py3
-rw-r--r--mesonbuild/minstall.py18
-rw-r--r--test cases/common/246 install_emptydir/meson.build4
-rw-r--r--test cases/common/246 install_emptydir/test.json7
12 files changed, 125 insertions, 5 deletions
diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim
index 15f5232..3a6a7de 100644
--- a/data/syntax-highlighting/vim/syntax/meson.vim
+++ b/data/syntax-highlighting/vim/syntax/meson.vim
@@ -97,6 +97,7 @@ syn keyword mesonBuiltin
\ install_headers
\ install_man
\ install_subdir
+ \ install_emptydir
\ is_disabler
\ is_variable
\ jar
diff --git a/docs/markdown/snippets/install_emptydir.md b/docs/markdown/snippets/install_emptydir.md
new file mode 100644
index 0000000..baedf58
--- /dev/null
+++ b/docs/markdown/snippets/install_emptydir.md
@@ -0,0 +1,18 @@
+## install_emptydir function
+
+It is now possible to define a directory which will be created during
+installation, without creating it as a side effect of installing files into it.
+This replaces custom `meson.add_install_script()` routines. For example:
+
+```meson
+meson.add_install_script('sh', '-c', 'mkdir -p "$DESTDIR/@0@"'.format(path))
+```
+
+can be replaced by:
+
+```meson
+install_emptydir(path)
+```
+
+and as a bonus this works reliably on Windows, prints a sensible progress
+message, will be uninstalled by `ninja uninstall`, etc.
diff --git a/docs/yaml/functions/install_emptydir.yaml b/docs/yaml/functions/install_emptydir.yaml
new file mode 100644
index 0000000..9c874e2
--- /dev/null
+++ b/docs/yaml/functions/install_emptydir.yaml
@@ -0,0 +1,27 @@
+name: install_emptydir
+returns: void
+since: 0.60.0
+description: |
+ Installs a new directory entry to the location specified by the positional
+ argument. If the directory exists and is not empty, the contents are left in
+ place.
+
+varargs:
+ name: dirpath
+ type: str
+ description: Directory to create during installation.
+
+kwargs:
+ install_mode:
+ type: list[str | int]
+ description: |
+ Specify the file mode in symbolic format and optionally the owner/uid and
+ group/gid for the created directory.
+
+ See the `install_mode` kwarg of [[install_data]] for more information.
+ install_tag:
+ type: str
+ description: |
+ A string used by the `meson install --tags` command to install only a
+ subset of the files. By default this directory has no install tag which
+ means it is not installed when the `--tags` argument is specified.
diff --git a/docs/yaml/functions/install_subdir.yaml b/docs/yaml/functions/install_subdir.yaml
index 83df782..90baed2 100644
--- a/docs/yaml/functions/install_subdir.yaml
+++ b/docs/yaml/functions/install_subdir.yaml
@@ -5,9 +5,12 @@ description: |
source tree to the location specified by the keyword argument
`install_dir`.
- If the subdirectory does not exist in the source tree, an empty directory is
- created in the specified location. *(since 0.45.0)* A newly created
- subdirectory may only be created in the keyword argument `install_dir`.
+ *(since 0.45.0, deprecated since 0.60.0)* If the subdirectory does not exist
+ in the source tree, an empty directory is created in the specified location.
+ A newly created subdirectory may only be created in the keyword argument
+ `install_dir`. There are a number of flaws with this method, and it was never
+ intentionally designed to work this way, please use [[install_emptydir]]
+ instead.
example: |
For a given directory `foo`:
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 7954b9b..ec93ff5 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -103,6 +103,7 @@ class AstInterpreter(InterpreterBase):
'install_man': self.func_do_nothing,
'install_data': self.func_do_nothing,
'install_subdir': self.func_do_nothing,
+ 'install_emptydir': self.func_do_nothing,
'configuration_data': self.func_do_nothing,
'configure_file': self.func_do_nothing,
'find_program': self.func_do_nothing,
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 6218156..3d8654e 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -120,6 +120,7 @@ class InstallData:
self.targets: T.List[TargetInstallData] = []
self.headers: T.List[InstallDataBase] = []
self.man: T.List[InstallDataBase] = []
+ self.emptydir: T.List[InstallEmptyDir] = []
self.data: T.List[InstallDataBase] = []
self.install_scripts: T.List[ExecutableSerialisation] = []
self.install_subdirs: T.List[SubdirInstallData] = []
@@ -147,6 +148,13 @@ class TargetInstallData:
self.optional = optional
self.tag = tag
+class InstallEmptyDir:
+ def __init__(self, path: str, install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None):
+ self.path = path
+ self.install_mode = install_mode
+ self.subproject = subproject
+ self.tag = tag
+
class InstallDataBase:
def __init__(self, path: str, install_path: str, install_path_name: str,
install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None,
@@ -1470,6 +1478,7 @@ class Backend:
self.generate_target_install(d)
self.generate_header_install(d)
self.generate_man_install(d)
+ self.generate_emptydir_install(d)
self.generate_data_install(d)
self.generate_custom_install_script(d)
self.generate_subdir_install(d)
@@ -1665,6 +1674,12 @@ class Backend:
i = InstallDataBase(srcabs, dstabs, dstname, m.get_custom_install_mode(), m.subproject, tag='man')
d.man.append(i)
+ def generate_emptydir_install(self, d: InstallData) -> None:
+ emptydir: T.List[build.EmptyDir] = self.build.get_emptydir()
+ for e in emptydir:
+ i = InstallEmptyDir(e.path, e.install_mode, e.subproject, e.install_tag)
+ d.emptydir.append(i)
+
def generate_data_install(self, d: InstallData) -> None:
data = self.build.get_data()
srcdir = self.environment.get_source_dir()
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 311732e..ef2210e 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -187,6 +187,16 @@ class Man(HoldableObject):
return self.sources
+class EmptyDir(HoldableObject):
+
+ def __init__(self, path: str, install_mode: 'FileMode', subproject: str,
+ install_tag: T.Optional[str] = None):
+ self.path = path
+ self.install_mode = install_mode
+ self.subproject = subproject
+ self.install_tag = install_tag
+
+
class InstallDir(HoldableObject):
def __init__(self, src_subdir: str, inst_subdir: str, install_dir: str,
@@ -239,6 +249,7 @@ class Build:
self.benchmarks: T.List['Test'] = []
self.headers: T.List[Headers] = []
self.man: T.List[Man] = []
+ self.emptydir: T.List[EmptyDir] = []
self.data: T.List[Data] = []
self.static_linker: PerMachine[StaticLinker] = PerMachine(None, None)
self.subprojects = {}
@@ -316,6 +327,9 @@ class Build:
def get_data(self) -> T.List['Data']:
return self.data
+ def get_emptydir(self) -> T.List['EmptyDir']:
+ return self.emptydir
+
def get_install_subdirs(self) -> T.List['InstallDir']:
return self.install_dirs
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 9a89b0c..8ac92c8 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -349,6 +349,7 @@ class Interpreter(InterpreterBase, HoldableObject):
'install_data': self.func_install_data,
'install_headers': self.func_install_headers,
'install_man': self.func_install_man,
+ 'install_emptydir': self.func_install_emptydir,
'install_subdir': self.func_install_subdir,
'is_disabler': self.func_is_disabler,
'is_variable': self.func_is_variable,
@@ -410,6 +411,7 @@ class Interpreter(InterpreterBase, HoldableObject):
build.AliasTarget: OBJ.AliasTargetHolder,
build.Headers: OBJ.HeadersHolder,
build.Man: OBJ.ManHolder,
+ build.EmptyDir: OBJ.EmptyDirHolder,
build.Data: OBJ.DataHolder,
build.InstallDir: OBJ.InstallDirHolder,
build.IncludeDirs: OBJ.IncludeDirsHolder,
@@ -1896,6 +1898,17 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
return m
+ @FeatureNew('install_emptydir', '0.60.0')
+ @typed_kwargs(
+ 'install_emptydir',
+ INSTALL_MODE_KW
+ )
+ def func_install_emptydir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs) -> None:
+ d = build.EmptyDir(args[0], kwargs['install_mode'], self.subproject)
+ self.build.emptydir.append(d)
+
+ return d
+
@FeatureNewKwargs('subdir', '0.44.0', ['if_found'])
@permittedKwargs({'if_found'})
@typed_pos_args('subdir', str)
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index 072d2f0..b0447e6 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -646,6 +646,9 @@ class InstallDirHolder(ObjectHolder[build.InstallDir]):
class ManHolder(ObjectHolder[build.Man]):
pass
+class EmptyDirHolder(ObjectHolder[build.EmptyDir]):
+ pass
+
class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]):
pass
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
index 6284f95..7864742 100644
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -25,7 +25,7 @@ import sys
import typing as T
from . import environment
-from .backend.backends import InstallData, InstallDataBase, TargetInstallData, ExecutableSerialisation
+from .backend.backends import InstallData, InstallDataBase, InstallEmptyDir, TargetInstallData, ExecutableSerialisation
from .coredata import major_versions_differ, MesonVersionMismatchException
from .coredata import version as coredata_version
from .mesonlib import Popen_safe, RealPathAction, is_windows
@@ -370,7 +370,7 @@ class Installer:
return run_exe(*args, **kwargs)
return 0
- def should_install(self, d: T.Union[TargetInstallData, InstallDataBase, ExecutableSerialisation]) -> bool:
+ def should_install(self, d: T.Union[TargetInstallData, InstallEmptyDir, InstallDataBase, ExecutableSerialisation]) -> bool:
if d.subproject and (d.subproject in self.skip_subprojects or '*' in self.skip_subprojects):
return False
if self.tags and d.tag not in self.tags:
@@ -531,6 +531,7 @@ class Installer:
self.install_targets(d, dm, destdir, fullprefix)
self.install_headers(d, dm, destdir, fullprefix)
self.install_man(d, dm, destdir, fullprefix)
+ self.install_emptydir(d, dm, destdir, fullprefix)
self.install_data(d, dm, destdir, fullprefix)
self.restore_selinux_contexts(destdir)
self.apply_ldconfig(destdir)
@@ -581,6 +582,19 @@ class Installer:
self.did_install_something = True
self.set_mode(outfilename, m.install_mode, d.install_umask)
+ def install_emptydir(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
+ for e in d.emptydir:
+ if not self.should_install(e):
+ continue
+ self.did_install_something = True
+ full_dst_dir = get_destdir_path(destdir, fullprefix, e.path)
+ self.log(f'Installing new directory {full_dst_dir}')
+ if os.path.isfile(full_dst_dir):
+ print(f'Tried to create directory {full_dst_dir} but a file of that name already exists.')
+ sys.exit(1)
+ dm.makedirs(full_dst_dir, exist_ok=True)
+ self.set_mode(full_dst_dir, e.install_mode, d.install_umask)
+
def install_headers(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for t in d.headers:
if not self.should_install(t):
diff --git a/test cases/common/246 install_emptydir/meson.build b/test cases/common/246 install_emptydir/meson.build
new file mode 100644
index 0000000..a5eb046
--- /dev/null
+++ b/test cases/common/246 install_emptydir/meson.build
@@ -0,0 +1,4 @@
+project('install_emptydir')
+
+install_emptydir(get_option('datadir')/'new_directory', install_mode: 'rwx------')
+install_emptydir(get_option('datadir')/'new_directory/subdir', install_mode: 'rwxr-----')
diff --git a/test cases/common/246 install_emptydir/test.json b/test cases/common/246 install_emptydir/test.json
new file mode 100644
index 0000000..17abe74
--- /dev/null
+++ b/test cases/common/246 install_emptydir/test.json
@@ -0,0 +1,7 @@
+{
+ "installed": [
+ { "type": "dir", "file": "usr/share/new_directory" },
+ { "type": "dir", "file": "usr/share/new_directory/subdir" }
+ ]
+}
+