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

WIP: cargo: Support building multiple versions of the same crate #12363

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4e03af5
build: Warning message was not telling which target it links to.
xclaesse Nov 23, 2023
b30f6e3
compilers: Every compiler can run code
xclaesse Nov 3, 2023
e48ba1c
compilers: Do not dump File content in log for compiler checks.
xclaesse Nov 6, 2023
84e2002
compilers: Allow setting env and workdir for run checks
xclaesse Nov 6, 2023
f7c9b9a
modules: Add helper to add project/global arguments
xclaesse Nov 3, 2023
6ede467
cargo: Ensure Dependency.package always has a value
xclaesse Oct 11, 2023
fd1cd06
cargo: Add API version into dependency name
xclaesse Oct 11, 2023
2455524
cargo: Generate .wrap file from crates.io dependencies
xclaesse Oct 11, 2023
11d0f66
cargo: Add patch_directory into generated wrap if available
xclaesse Oct 11, 2023
4372383
cargo: Fix cfg() parsing
xclaesse Nov 1, 2023
8ffa42d
cargo: builder: Add elseblock support
xclaesse Nov 1, 2023
ffa0a19
cargo: Split function that adds a single dependency
xclaesse Nov 1, 2023
b22d37c
rust: Add get_target_triplet() method
xclaesse Nov 6, 2023
06ba9b7
rust: Add rust.cargo_cfg() module method
xclaesse Nov 1, 2023
65a079f
cargo: Fix error when defining options on build-dependencies
xclaesse Nov 22, 2023
2137f45
cargo: Only build one crate type at a time
xclaesse Nov 24, 2023
37df695
cargo: Set CARGO_PKG_* in env when building and running build.rs
xclaesse Nov 24, 2023
a6dd090
cargo: Warn if project has build-dependencies
xclaesse Feb 23, 2024
ad49e05
rust: recursively pull proc-macro dependencies as well
xclaesse Feb 27, 2024
600d1e6
cargo: Add support for `system-deps` dependencies
thiblahute Apr 22, 2023
18bd1ec
WIP: workspace support and fixes
xclaesse Mar 4, 2024
f7621ee
cargo: Override cargo provided dependencies with latest version
xclaesse Mar 4, 2024
cf0224b
cargo: A dependency is required if a feature with same name is enabled
xclaesse Mar 4, 2024
6a3f030
cargo: Fix crate name VS package name VS dependency name confusion
xclaesse Mar 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/markdown/Rust-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,19 @@ Only a subset of [[shared_library]] keyword arguments are allowed:
- link_depends
- link_with
- override_options

### cfg()

```meson
rustmod.cfg(name)
```

*Since 1.4.0*

Returns the value of a Rust compiler config. Configs can either be predefined by
the Rust compiler (see `rustc --print cfg`) or set by the user with
`--cfg name="value"` in `RUSTFLAGS` environment or `rust_args` option.

If the config is set in the form `name="value"` then `value` string is returned.
If it is set with no value, `true` is returned. If it is not set, `false` is
returned.
9 changes: 9 additions & 0 deletions docs/markdown/snippets/rust_cfg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## New `rust.cfg()` module method

Returns the value of a Rust compiler config. Configs can either be predefined by
the Rust compiler (see `rustc --print cfg`) or set by the user with
`--cfg name="value"` in `RUSTFLAGS` environment or `rust_args` option.

If the config is set in the form `name="value"` then `value` string is returned.
If it is set with no value, `true` is returned. If it is not set, `false` is
returned.
4 changes: 1 addition & 3 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1295,8 +1295,6 @@ def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], include
for t in self.link_targets:
if t in result:
continue
if t.rust_crate_type == 'proc-macro':
continue
if include_internals or not t.is_internal():
result.add(t)
if isinstance(t, StaticLibrary):
Expand Down Expand Up @@ -1483,7 +1481,7 @@ def check_can_link_together(self, t: BuildTargetTypes) -> None:
if not self.uses_rust() and links_with_rust_abi:
raise InvalidArguments(f'Try to link Rust ABI library {t.name!r} with a non-Rust target {self.name!r}')
if self.for_machine is not t.for_machine and (not links_with_rust_abi or t.rust_crate_type != 'proc-macro'):
msg = f'Tried to tied to mix a {t.for_machine} library ("{t.name}") with a {self.for_machine} target "{self.name}"'
msg = f'Tried to link {self.name!r} for {self.for_machine} with {t.name!r} for {t.for_machine}.'
if self.environment.is_cross_build():
raise InvalidArguments(msg + ' This is not possible in a cross build.')
else:
Expand Down
5 changes: 3 additions & 2 deletions mesonbuild/cargo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__all__ = [
'interpret'
'interpret',
'dependencies',
]

from .interpreter import interpret
from .interpreter import interpret, dependencies
4 changes: 2 additions & 2 deletions mesonbuild/cargo/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def plusassign(self, value: mparser.BaseNode, varname: str) -> mparser.PlusAssig
"""
return mparser.PlusAssignmentNode(self.identifier(varname), self._symbol('+='), value)

def if_(self, condition: mparser.BaseNode, block: mparser.CodeBlockNode) -> mparser.IfClauseNode:
def if_(self, condition: mparser.BaseNode, block: mparser.CodeBlockNode, elseblock: T.Optional[mparser.CodeBlockNode] = None) -> mparser.IfClauseNode:
"""Create a "if" block

:param condition: The condition
Expand All @@ -222,7 +222,7 @@ def if_(self, condition: mparser.BaseNode, block: mparser.CodeBlockNode) -> mpar
"""
clause = mparser.IfClauseNode(condition)
clause.ifs.append(mparser.IfNode(clause, self._symbol('if'), condition, block))
clause.elseblock = mparser.EmptyNode(-1, -1, self.filename)
clause.elseblock = mparser.ElseNode(self._symbol('else'), elseblock) if elseblock else mparser.EmptyNode(-1, -1, self.filename)
return clause

def foreach(self, varnames: T.List[str], items: mparser.BaseNode, block: mparser.CodeBlockNode) -> mparser.ForeachClauseNode:
Expand Down
124 changes: 63 additions & 61 deletions mesonbuild/cargo/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""Rust CFG parser.

Rust uses its `cfg()` format in cargo.
https://doc.rust-lang.org/reference/conditional-compilation.html

This may have the following functions:
- all()
Expand Down Expand Up @@ -33,7 +34,7 @@
if T.TYPE_CHECKING:
_T = T.TypeVar('_T')
_LEX_TOKEN = T.Tuple['TokenType', T.Optional[str]]
_LEX_STREAM = T.Iterable[_LEX_TOKEN]
_LEX_STREAM = T.Iterator[_LEX_TOKEN]
_LEX_STREAM_AH = T.Iterator[T.Tuple[_LEX_TOKEN, T.Optional[_LEX_TOKEN]]]


Expand All @@ -48,6 +49,7 @@ class TokenType(enum.Enum):
NOT = enum.auto()
COMMA = enum.auto()
EQUAL = enum.auto()
CFG = enum.auto()


def lexer(raw: str) -> _LEX_STREAM:
Expand All @@ -56,45 +58,41 @@ def lexer(raw: str) -> _LEX_STREAM:
:param raw: The raw cfg() expression
:return: An iterable of tokens
"""
buffer: T.List[str] = []
start: int = 0
is_string: bool = False
for s in raw:
if s.isspace() or s in {')', '(', ',', '='} or (s == '"' and buffer):
val = ''.join(buffer)
buffer.clear()
if is_string:
for i, s in enumerate(raw):
if s.isspace() or s in {')', '(', ',', '=', '"'}:
val = raw[start:i]
start = i + 1
if s == '"' and is_string:
yield (TokenType.STRING, val)
is_string = False
continue
elif val == 'any':
yield (TokenType.ANY, None)
elif val == 'all':
yield (TokenType.ALL, None)
elif val == 'not':
yield (TokenType.NOT, None)
elif val == 'cfg':
yield (TokenType.CFG, None)
elif val:
yield (TokenType.IDENTIFIER, val)

if s == '(':
yield (TokenType.LPAREN, None)
continue
elif s == ')':
yield (TokenType.RPAREN, None)
continue
elif s == ',':
yield (TokenType.COMMA, None)
continue
elif s == '=':
yield (TokenType.EQUAL, None)
continue
elif s.isspace():
continue

if s == '"':
is_string = not is_string
else:
buffer.append(s)
if buffer:
elif s == '"':
is_string = True
val = raw[start:]
if val:
# This should always be an identifier
yield (TokenType.IDENTIFIER, ''.join(buffer))
yield (TokenType.IDENTIFIER, val)


def lookahead(iter: T.Iterator[_T]) -> T.Iterator[T.Tuple[_T, T.Optional[_T]]]:
Expand Down Expand Up @@ -146,8 +144,8 @@ class Identifier(IR):
@dataclasses.dataclass
class Equal(IR):

lhs: IR
rhs: IR
lhs: Identifier
rhs: String


@dataclasses.dataclass
Expand Down Expand Up @@ -175,41 +173,40 @@ def _parse(ast: _LEX_STREAM_AH) -> IR:
else:
ntoken, _ = (None, None)

stream: T.List[_LEX_TOKEN]
if token is TokenType.IDENTIFIER:
assert value
id_ = Identifier(value)
if ntoken is TokenType.EQUAL:
return Equal(Identifier(value), _parse(ast))
if token is TokenType.STRING:
return String(value)
if token is TokenType.EQUAL:
# In this case the previous caller already has handled the equal
return _parse(ast)
if token in {TokenType.ANY, TokenType.ALL}:
next(ast)
(token, value), _ = next(ast)
assert token is TokenType.STRING
assert value is not None
return Equal(id_, String(value))
return id_
elif token in {TokenType.ANY, TokenType.ALL}:
type_ = All if token is TokenType.ALL else Any
assert ntoken is TokenType.LPAREN
next(ast) # advance the iterator to get rid of the LPAREN
stream = []
args: T.List[IR] = []
while token is not TokenType.RPAREN:
(token, value), n_stream = next(ast)
assert token is TokenType.LPAREN
if n_stream and n_stream[0] == TokenType.RPAREN:
return type_(args)
while True:
args.append(_parse(ast))
(token, value), _ = next(ast)
if token is TokenType.COMMA:
args.append(_parse(lookahead(iter(stream))))
stream.clear()
else:
stream.append((token, value))
if stream:
args.append(_parse(lookahead(iter(stream))))
if token is TokenType.RPAREN:
break
assert token is TokenType.COMMA
return type_(args)
if token is TokenType.NOT:
next(ast) # advance the iterator to get rid of the LPAREN
stream = []
# Mypy can't figure out that token is overridden inside the while loop
while token is not TokenType.RPAREN: # type: ignore
(token, value), _ = next(ast)
stream.append((token, value))
return Not(_parse(lookahead(iter(stream))))

raise MesonBugException(f'Unhandled Cargo token: {token}')
elif token in {TokenType.NOT, TokenType.CFG}:
is_not = token is TokenType.NOT
(token, value), _ = next(ast)
assert token is TokenType.LPAREN
arg = _parse(ast)
(token, value), _ = next(ast)
assert token is TokenType.RPAREN
return Not(arg) if is_not else arg
else:
raise MesonBugException(f'Unhandled Cargo token:{token} {value}')


def parse(ast: _LEX_STREAM) -> IR:
Expand All @@ -218,7 +215,7 @@ def parse(ast: _LEX_STREAM) -> IR:
:param ast: An iterable of Tokens
:return: An mparser Node to be used as a conditional
"""
ast_i: _LEX_STREAM_AH = lookahead(iter(ast))
ast_i: _LEX_STREAM_AH = lookahead(ast)
return _parse(ast_i)


Expand All @@ -234,19 +231,16 @@ def _(ir: String, build: builder.Builder) -> mparser.BaseNode:

@ir_to_meson.register
def _(ir: Identifier, build: builder.Builder) -> mparser.BaseNode:
host_machine = build.identifier('host_machine')
if ir.value == "target_arch":
return build.method('cpu_family', host_machine)
elif ir.value in {"target_os", "target_family"}:
return build.method('system', host_machine)
elif ir.value == "target_endian":
return build.method('endian', host_machine)
raise MesonBugException(f"Unhandled Cargo identifier: {ir.value}")
# cfg.has_key('identifier')
return build.method('has_key', build.identifier('cfg'), [build.string(ir.value)])


@ir_to_meson.register
def _(ir: Equal, build: builder.Builder) -> mparser.BaseNode:
return build.equal(ir_to_meson(ir.lhs, build), ir_to_meson(ir.rhs, build))
# cfg.get('identifier', '') == 'value'
return build.equal(
build.method('get', build.identifier('cfg'), [build.string(ir.lhs.value), build.string('')]),
build.string(ir.rhs.value))


@ir_to_meson.register
Expand All @@ -256,6 +250,8 @@ def _(ir: Not, build: builder.Builder) -> mparser.BaseNode:

@ir_to_meson.register
def _(ir: Any, build: builder.Builder) -> mparser.BaseNode:
if not ir.args:
return build.bool(False)
args = iter(reversed(ir.args))
last = next(args)
cur = build.or_(ir_to_meson(next(args), build), ir_to_meson(last, build))
Expand All @@ -266,9 +262,15 @@ def _(ir: Any, build: builder.Builder) -> mparser.BaseNode:

@ir_to_meson.register
def _(ir: All, build: builder.Builder) -> mparser.BaseNode:
if not ir.args:
return build.bool(True)
args = iter(reversed(ir.args))
last = next(args)
cur = build.and_(ir_to_meson(next(args), build), ir_to_meson(last, build))
for a in args:
cur = build.and_(ir_to_meson(a, build), cur)
return cur


def cfg_to_meson(raw: str, build: builder.Builder) -> mparser.BaseNode:
return ir_to_meson(parse(lexer(raw)), build)
Loading
Loading