-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Support user-specified config-tool programs #13660
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
## New user-specified config tool support | ||
|
||
The `method` keyword for `dependency()` can now take an external program as | ||
an argument: | ||
|
||
```meson | ||
config_tool = find_program('my-config-tool') | ||
dep = dependency('some-dep', method: config_tool) | ||
|
||
# or pass required arguments to it | ||
dep = dependency('some-dep', method: [config_tool, '--package', '@NAME']) | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
|
||
from .base import ExternalDependency, DependencyException, DependencyTypeName | ||
from ..mesonlib import listify, Popen_safe, Popen_safe_logged, split_args, version_compare, version_compare_many | ||
from ..programs import find_external_program | ||
from ..programs import find_external_program, ExternalProgram | ||
from .. import mlog | ||
import re | ||
import typing as T | ||
|
@@ -52,7 +52,7 @@ | |
req_version = mesonlib.stringlistify(req_version_raw) | ||
else: | ||
req_version = [] | ||
tool, version = self.find_config(req_version, kwargs.get('returncode_value', 0)) | ||
Check warning Code scanning / CodeQL `__init__` method calls overridden method Warning
Call to self.
find_config Error loading related location Loading method UserConfigToolDependency.find_config Error loading related location Loading Call to self. find_config Error loading related location Loading method GnuStepDependency.find_config Error loading related location Loading |
||
self.config = tool | ||
self.is_found = self.report_config(version, req_version) | ||
if not self.is_found: | ||
|
@@ -78,35 +78,11 @@ | |
for potential_bin in find_external_program( | ||
self.env, self.for_machine, self.tool_name, | ||
self.tool_name, self.tools, allow_default_for_cross=self.allow_default_for_cross): | ||
if not potential_bin.found(): | ||
tool, out = self._check_config(potential_bin, [], versions, returncode) | ||
if tool is None and out is None: | ||
continue | ||
tool = potential_bin.get_command() | ||
try: | ||
p, out = Popen_safe(tool + [self.version_arg])[:2] | ||
except (FileNotFoundError, PermissionError): | ||
continue | ||
if p.returncode != returncode: | ||
if self.skip_version: | ||
# maybe the executable is valid even if it doesn't support --version | ||
p = Popen_safe(tool + [self.skip_version])[0] | ||
if p.returncode != returncode: | ||
continue | ||
else: | ||
continue | ||
|
||
out = self._sanitize_version(out.strip()) | ||
# Some tools, like pcap-config don't supply a version, but also | ||
# don't fail with --version, in that case just assume that there is | ||
# only one version and return it. | ||
if not out: | ||
elif out is None: | ||
return (tool, None) | ||
if versions: | ||
is_found = version_compare_many(out, versions)[0] | ||
# This allows returning a found version without a config tool, | ||
# which is useful to inform the user that you found version x, | ||
# but y was required. | ||
if not is_found: | ||
tool = None | ||
if best_match[1]: | ||
if version_compare(out, '> {}'.format(best_match[1])): | ||
best_match = (tool, out) | ||
|
@@ -115,6 +91,39 @@ | |
|
||
return best_match | ||
|
||
def _check_config(self, potential_bin: ExternalProgram, args: T.List[str], versions: T.List[str], returncode: int = 0) \ | ||
-> T.Tuple[T.Optional[T.List[str]], T.Optional[str]]: | ||
if not potential_bin.found(): | ||
return (None, None) | ||
tool = potential_bin.get_command() + args | ||
try: | ||
p, out = Popen_safe(tool + [self.version_arg])[:2] | ||
except (FileNotFoundError, PermissionError): | ||
return (None, None) | ||
if p.returncode != returncode: | ||
if self.skip_version: | ||
# maybe the executable is valid even if it doesn't support --version | ||
p = Popen_safe(tool + [self.skip_version])[0] | ||
if p.returncode != returncode: | ||
return (None, None) | ||
else: | ||
return (None, None) | ||
|
||
out = self._sanitize_version(out.strip()) | ||
# Some tools, like pcap-config don't supply a version, but also | ||
# don't fail with --version, in that case just assume that there is | ||
# only one version and return it. | ||
if not out: | ||
return (tool, None) | ||
if versions: | ||
is_found = version_compare_many(out, versions)[0] | ||
# This allows returning a found version without a config tool, | ||
# which is useful to inform the user that you found version x, | ||
# but y was required. | ||
if not is_found: | ||
tool = None | ||
return tool, out | ||
Comment on lines
+112
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is going to be done, I think it'd be great to take the opportunity to stop calling the version variable |
||
|
||
def report_config(self, version: T.Optional[str], req_version: T.List[str]) -> bool: | ||
"""Helper method to print messages about the tool.""" | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# User-specified config-tool that has custom positional arguments. must implement --cflags, | ||
# --libs, and --version | ||
# | ||
# Note: meson will not reconfigure if this program or its output changes | ||
# | ||
|
||
import pathlib | ||
import platform | ||
import sys | ||
|
||
|
||
def make_include_arg(p: pathlib.Path) -> str: | ||
if platform.system().lower() == "windows": | ||
return f'"-I{p.absolute()}"' | ||
else: | ||
return f"'-I{p.absolute()}'" | ||
|
||
|
||
if __name__ == '__main__': | ||
_, module, flag = sys.argv | ||
|
||
# pretend we can only find somemod module | ||
if module != 'somemod': | ||
sys.exit(1) | ||
|
||
if flag == '--cflags': | ||
somemod = pathlib.Path(__file__).parent / 'somemod' | ||
print(make_include_arg(somemod)) | ||
elif flag == '--libs': | ||
print() | ||
elif flag == '--version': | ||
print('43.0') | ||
else: | ||
sys.exit(1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# User-specified config-tool, must implement --cflags, --libs, and --version | ||
# | ||
# Note: meson will not reconfigure if this program or its output changes | ||
# | ||
|
||
import pathlib | ||
import platform | ||
import sys | ||
|
||
|
||
def make_include_arg(p: pathlib.Path) -> str: | ||
if platform.system().lower() == "windows": | ||
return f'"-I{p.absolute()}"' | ||
else: | ||
return f"'-I{p.absolute()}'" | ||
|
||
|
||
if __name__ == '__main__': | ||
flag = sys.argv[1] | ||
|
||
if flag == '--cflags': | ||
somedep = pathlib.Path(__file__).parent / 'somedep' | ||
print(make_include_arg(somedep)) | ||
elif flag == '--libs': | ||
print() | ||
elif flag == '--version': | ||
print('42.0') | ||
else: | ||
sys.exit(1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
#include "somedep.h" | ||
#include "somemod.h" | ||
|
||
int main(void) { | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of churn here, but it's just refactoring to the _check_config function. Open to ideas to make this churn less.