Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate configure_file(copy : true), and replace it with fs.copyfile #10042

Merged
merged 1 commit into from Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/markdown/Fs-module.md
Expand Up @@ -215,3 +215,30 @@ fs.stem('foo/bar/baz.dll.a') # baz.dll
specified by `path` changes, this will trigger Meson to reconfigure the
project. If the file specified by `path` is a `files()` object it
cannot refer to a built file.


### copyfile

*Since 0.64.0*

Copy a file from the source directory to the build directory at build time

Has the following positional arguments:
- src `File | str`: the file to copy

Has the following optional arguments:
- dest `str`: the name of the output file. If unset will be the basename of
the src argument

Has the following keyword arguments:
- install `bool`: Whether to install the copied file, defaults to false
- install_dir `str`: Where to install the file to
- install_tag: `str`: the install tag to assign to this target
- install_mode `array[str | int]`: the mode to install the file with

returns:
- a [[custom_target]] object

```meson
copy = fs.copyfile('input-file', 'output-file')
```
17 changes: 17 additions & 0 deletions docs/markdown/snippets/fs_copyfile.md
@@ -0,0 +1,17 @@
## `fs.copyfile` to replace `configure_file(copy : true)`

A new method has been added to the `fs` module, `copyfile`. This method replaces
`configure_file(copy : true)`, but only copies files. Unlike `configure_file()`
it runs at build time, and the output name is optional defaulting to the
filename without paths of the input if unset:

```meson
fs.copyfile('src/file.txt')
```
Will create a file in the current build directory called `file.txt`


```meson
fs.copyfile('file.txt', 'outfile.txt')
```
Will create a copy renamed to `outfile.txt`
5 changes: 4 additions & 1 deletion mesonbuild/interpreter/interpreter.py
Expand Up @@ -2453,7 +2453,10 @@ def func_install_subdir(self, node: mparser.BaseNode, args: T.Tuple[str],
'configuration',
(ContainerTypeInfo(dict, (str, int, bool)), build.ConfigurationData, NoneType),
),
KwargInfo('copy', bool, default=False, since='0.47.0'),
KwargInfo(
'copy', bool, default=False, since='0.47.0',
deprecated='0.64.0', deprecated_message='Use fs.copy instead',
),
KwargInfo('encoding', str, default='utf-8', since='0.47.0'),
KwargInfo('format', str, default='meson', since='0.46.0',
validator=in_set_validator({'meson', 'cmake', 'cmake@'})),
Expand Down
65 changes: 60 additions & 5 deletions mesonbuild/modules/fs.py
Expand Up @@ -12,24 +12,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import typing as T
from __future__ import annotations
from pathlib import Path, PurePath, PureWindowsPath
import hashlib
import os
from pathlib import Path, PurePath, PureWindowsPath
import typing as T

from . import ExtensionModule, ModuleReturnValue, ModuleInfo
from .. import mlog
from . import ExtensionModule, ModuleInfo
from ..build import CustomTarget, InvalidArguments
from ..interpreter.type_checking import INSTALL_KW, INSTALL_MODE_KW, INSTALL_TAG_KW, NoneType
from ..interpreterbase import FeatureNew, KwargInfo, typed_kwargs, typed_pos_args, noKwargs
from ..mesonlib import (
File,
MesonException,
has_path_sep,
path_is_in_root,
)
from ..interpreterbase import FeatureNew, KwargInfo, typed_kwargs, typed_pos_args, noKwargs

if T.TYPE_CHECKING:
from . import ModuleState
from ..interpreter import Interpreter
from ..mesonlib import FileOrString
from ..mesonlib import FileOrString, FileMode

from typing_extensions import TypedDict

Expand All @@ -38,6 +42,15 @@ class ReadKwArgs(TypedDict):

encoding: str

class CopyKw(TypedDict):

"""Kwargs for fs.copy"""

install: bool
install_dir: T.Optional[str]
install_mode: FileMode
install_tag: T.Optional[str]


class FSModule(ExtensionModule):

Expand All @@ -61,6 +74,7 @@ def __init__(self, interpreter: 'Interpreter') -> None:
'name': self.name,
'stem': self.stem,
'read': self.read,
'copyfile': self.copyfile,
})

def _absolute_dir(self, state: 'ModuleState', arg: 'FileOrString') -> Path:
Expand Down Expand Up @@ -255,6 +269,47 @@ def read(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: 'Rea
self.interpreter.add_build_def_file(path)
return data

@FeatureNew('fs.copyfile', '0.64.0')
@typed_pos_args('fs.copyfile', (File, str), optargs=[str])
@typed_kwargs(
'fs.copyfile',
INSTALL_KW,
INSTALL_MODE_KW,
INSTALL_TAG_KW,
KwargInfo('install_dir', (str, NoneType)),
)
def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[str]],
kwargs: CopyKw) -> ModuleReturnValue:
"""Copy a file into the build directory at build time."""
if kwargs['install'] and not kwargs['install_dir']:
raise InvalidArguments('"install_dir" must be specified when "install" is true')

src = self.interpreter.source_strings_to_files([args[0]])[0]

# The input is allowed to have path separators, but the output may not,
# so use the basename for the default case
dest = args[1] if args[1] else os.path.basename(src.fname)
if has_path_sep(dest):
raise InvalidArguments('Destination path may not have path separators')

ct = CustomTarget(
dest,
state.subdir,
state.subproject,
state.environment,
state.environment.get_build_command() + ['--internal', 'copy', '@INPUT@', '@OUTPUT@'],
[src],
[dest],
build_by_default=True,
install=kwargs['install'],
install_dir=kwargs['install_dir'],
install_mode=kwargs['install_mode'],
install_tag=kwargs['install_tag'],
backend=state.backend,
)

return ModuleReturnValue(ct, [ct])


def initialize(*args: T.Any, **kwargs: T.Any) -> FSModule:
return FSModule(*args, **kwargs)
22 changes: 21 additions & 1 deletion test cases/common/14 configure file/meson.build
@@ -1,4 +1,6 @@
project('configure file test', 'c', meson_version: '>=0.47.0')
project('configure file test', 'c', meson_version: '>=0.63.0')

fs = import('fs')

conf = configuration_data()

Expand Down Expand Up @@ -188,6 +190,9 @@ if ret.returncode() != 0
endif
# Now the same, but using a File object as an argument.
inf2 = files('invalid-utf8.bin.in')[0]
outf = configure_file(input : inf2,
output : 'invalid-utf8.bin',
copy: true)
ret = run_command(check_file, inf2, outf, check: false)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
Expand All @@ -202,6 +207,21 @@ if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif

# Test the fs replacement
# Test copying of an empty configuration data object
inf = 'invalid-utf8.bin.in'
outf = fs.copyfile(inf, 'invalid-utf8-1.bin')
test('fs.copyfile string', check_file, args: [files(inf), outf])

# Test with default outname of string
outf = fs.copyfile(inf)
test('fs.copyfile default name', check_file, args: [files(inf), outf])

# Now the same, but using a File object as an argument.
inf2 = files('invalid-utf8.bin.in')[0]
outf = fs.copyfile(inf2, 'invalid-utf8-2.bin')
test('fs.copyfile file', check_file, args: [inf2, outf])

# Test non isolatin1 encoded input file which fails to decode with utf-8
conf8 = configuration_data()
conf8.set('var', 'foo')
Expand Down