Compare commits

...

8 Commits

Author SHA1 Message Date
Jussi Pakkanen 46788d1b5b Created the filesystem module. 5 years ago
Vedran Miletić 48a719033e docs: Mention error LNK1181, linking for MSVC [skip ci] 5 years ago
Camilo Celis Guzman 7a76fecdf7 mesonbuild/mtest: plumb and report a test's start time 5 years ago
Ryan Lucia 8987a39675 pkgconfig: only check import_filename for shared libraries 5 years ago
Michael Hirsch, Ph.D 6e708208dd CI: add initial type annotation checking 5 years ago
Michael Hirsch, Ph.D d080917561 wrap.py: catch connection error with WrapException 5 years ago
Michael Hirsch, Ph.D a47c1374b9 wrap.py: apply type annotation, modernize syntax 5 years ago
Liam Beguin 30acd94e68 syntax-highlighting: vim: fix setting cpo [skip ci] 5 years ago
  1. 7
      azure-pipelines.yml
  2. 2
      data/syntax-highlighting/vim/syntax/meson.vim
  3. 2
      docs/markdown/FAQ.md
  4. 31
      docs/markdown/Fs-module.md
  5. 10
      docs/markdown/snippets/fsmodule.md
  6. 1
      docs/sitemap.txt
  7. 3
      mesonbuild/interpreter.py
  8. 2
      mesonbuild/minit.py
  9. 17
      mesonbuild/modules/__init__.py
  10. 59
      mesonbuild/modules/fs.py
  11. 2
      mesonbuild/modules/pkgconfig.py
  12. 14
      mesonbuild/msetup.py
  13. 34
      mesonbuild/mtest.py
  14. 4
      mesonbuild/wrap/__init__.py
  15. 91
      mesonbuild/wrap/wrap.py
  16. 43
      mesonbuild/wrap/wraptool.py
  17. 1
      test cases/common/227 fs module/a_symlink
  18. 21
      test cases/common/227 fs module/meson.build
  19. 1
      test cases/common/227 fs module/subdir/meson.build
  20. 1
      test cases/common/227 fs module/subdir/subdirfile.txt
  21. 9
      test cases/common/227 fs module/subprojects/subbie/meson.build
  22. 1
      test cases/common/227 fs module/subprojects/subbie/subprojectfile.txt
  23. 1
      test cases/common/227 fs module/subprojects/subbie/subsub/meson.build
  24. 1
      test cases/common/227 fs module/subprojects/subbie/subsub/subsubfile.txt
  25. 2
      test cases/common/91 dep fallback/tester.c
  26. 14
      tools/ac_converter.py
  27. 2
      tools/boost_names.py
  28. 21
      tools/dircondenser.py

@ -11,7 +11,7 @@ variables:
CI: 1
jobs:
- job: Pylint
- job: PylintMyPy
pool:
vmImage: ubuntu-latest
@ -20,10 +20,11 @@ jobs:
inputs:
versionSpec: '3.7'
addToPath: true
- script: python -m pip install pylint
displayName: Install Pylint
- script: python -m pip install pylint mypy
displayName: 'Install Pylint, MyPy'
- script: pylint mesonbuild
displayName: Lint Checks
- script: mypy --follow-imports=skip mesonbuild/mtest.py mesonbuild/minit.py mesonbuild/msetup.py mesonbuild/wrap tools/
- job: vs2015

@ -28,7 +28,7 @@ endif
" We need nocompatible mode in order to continue lines with backslashes.
" Original setting will be restored.
let s:cpo_save = &cpo
setlocal cpo&vim
set cpo&vim
" http://mesonbuild.com/Syntax.html
syn keyword mesonConditional elif else if endif

@ -187,7 +187,7 @@ file instead of being buried inside your build definitions. An example
can be found
[here](https://github.com/jpakkane/meson/tree/master/test%20cases/linuxlike/3%20linker%20script).
## My project works fine on Linux and MinGW but fails with MSVC due to a missing .lib file
## My project works fine on Linux and MinGW but fails to link with MSVC due to a missing .lib file (fatal error LNK1181). Why?
With GCC, all symbols on shared libraries are exported automatically
unless you specify otherwise. With MSVC no symbols are exported by

@ -0,0 +1,31 @@
# FS (filesystem) module
This module provides functions to inspect the file system. It is
available starting with version 0.53.0.
## File lookup rules
Non-absolute paths are looked up relative to the directory where the
current `meson.build` file is.
### exists
Takes a single string argument and returns true if an entity with that
name exists on the file system. This can be a file, directory or a
special entry such as a device node.
### is_dir
Takes a single string argument and returns true if a directory with
that name exists on the file system. This method follows symbolic
links.
### is_file
Takes a single string argument and returns true if an file with that
name exists on the file system. This method follows symbolic links.
### is_symlink
Takes a single string argument and returns true if the path pointed to
by the string is a symbolic link.

@ -0,0 +1,10 @@
## A new module for filesystem operations
The new `fs` module can be used to examine the contents of the current
file system.
```meson
fs = import('fs')
assert(fs.exists('important_file'),
'The important file is missing.')
```

@ -32,6 +32,7 @@ index.md
Modules.md
CMake-module.md
Dlang-module.md
Fs-module.md
Gnome-module.md
Hotdoc-module.md
i18n-module.md

@ -1689,7 +1689,7 @@ class CompilerHolder(InterpreterObject):
ModuleState = namedtuple('ModuleState', [
'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
'source_root', 'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
'project_name', 'project_version', 'backend', 'targets',
'data', 'headers', 'man', 'global_args', 'project_args', 'build_machine',
'host_machine', 'target_machine', 'current_node'])
@ -1714,6 +1714,7 @@ class ModuleHolder(InterpreterObject, ObjectHolder):
# because the Build object contains dicts and lists.
num_targets = len(self.interpreter.build.targets)
state = ModuleState(
source_root = self.interpreter.environment.get_source_dir(),
build_to_src=mesonlib.relpath(self.interpreter.environment.get_source_dir(),
self.interpreter.environment.get_build_dir()),
subproject=self.interpreter.subproject,

@ -83,7 +83,7 @@ def create_sample(options):
raise RuntimeError('Unreachable code')
print(info_message)
def autodetect_options(options, sample=False):
def autodetect_options(options, sample: bool = False):
if not options.name:
options.name = Path().resolve().stem
if not re.match('[a-zA-Z_][a-zA-Z0-9]*', options.name) and sample:

@ -1,3 +1,20 @@
# Copyright 2019 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file contains the detection logic for external dependencies that
# are UI-related.
import os
from .. import build

@ -0,0 +1,59 @@
# Copyright 2019 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from . import ExtensionModule
from . import ModuleReturnValue
from ..mesonlib import MesonException
from ..interpreterbase import stringArgs, noKwargs
class FSModule(ExtensionModule):
def __init__(self, interpreter):
super().__init__(interpreter)
self.snippets.add('generate_dub_file')
@stringArgs
@noKwargs
def exists(self, state, args, kwargs):
if len(args) != 1:
MesonException('method takes exactly one argument.')
test_file = os.path.join(state.source_root, state.subdir, args[0])
return ModuleReturnValue(os.path.exists(test_file), [])
def _check(self, check_fun, state, args):
if len(args) != 1:
MesonException('method takes exactly one argument.')
test_file = os.path.join(state.source_root, state.subdir, args[0])
return ModuleReturnValue(check_fun(test_file), [])
@stringArgs
@noKwargs
def is_symlink(self, state, args, kwargs):
return self._check(os.path.islink, state, args)
@stringArgs
@noKwargs
def is_file(self, state, args, kwargs):
return self._check(os.path.isfile, state, args)
@stringArgs
@noKwargs
def is_dir(self, state, args, kwargs):
return self._check(os.path.isdir, state, args)
def initialize(*args, **kwargs):
return FSModule(*args, **kwargs)

@ -231,7 +231,7 @@ class PkgConfigModule(ExtensionModule):
return l.name[3:]
# If the library is imported via an import library which is always
# named after the target name, '-lfoo' is correct.
if l.import_filename:
if isinstance(l, build.SharedLibrary) and l.import_filename:
return l.name
# In other cases, we can't guarantee that the compiler will be able to
# find the library via '-lfoo', so tell the user that.

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import typing
import time
import sys, stat
import datetime
@ -96,11 +97,11 @@ class MesonApp:
self.options = options
def has_build_file(self, dirname):
def has_build_file(self, dirname: str) -> bool:
fname = os.path.join(dirname, environment.build_filename)
return os.path.exists(fname)
def validate_core_dirs(self, dir1, dir2):
def validate_core_dirs(self, dir1: str, dir2: str) -> typing.Tuple[str, str]:
if dir1 is None:
if dir2 is None:
if not os.path.exists('meson.build') and os.path.exists('../meson.build'):
@ -130,7 +131,7 @@ class MesonApp:
return ndir2, ndir1
raise MesonException('Neither directory contains a build file %s.' % environment.build_filename)
def validate_dirs(self, dir1, dir2, reconfigure, wipe):
def validate_dirs(self, dir1: str, dir2: str, reconfigure: bool, wipe: bool) -> typing.Tuple[str, str]:
(src_dir, build_dir) = self.validate_core_dirs(dir1, dir2)
priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat')
if os.path.exists(priv_dir):
@ -142,12 +143,11 @@ class MesonApp:
'\nIf build failures persist, run "meson setup --wipe" to rebuild from scratch\n'
'using the same options as passed when configuring the build.'
'\nTo change option values, run "meson configure" instead.')
sys.exit(0)
raise SystemExit
else:
has_cmd_line_file = os.path.exists(coredata.get_cmd_line_file(build_dir))
if (wipe and not has_cmd_line_file) or (not wipe and reconfigure):
print('Directory does not contain a valid build tree:\n{}'.format(build_dir))
sys.exit(1)
raise SystemExit('Directory does not contain a valid build tree:\n{}'.format(build_dir))
return src_dir, build_dir
def generate(self):
@ -239,7 +239,7 @@ class MesonApp:
os.unlink(cdf)
raise
def run(options):
def run(options) -> int:
coredata.parse_cmd_line_options(options)
app = MesonApp(options)
app.generate()

@ -320,8 +320,8 @@ class TestRun:
@classmethod
def make_exitcode(cls, test: 'TestSerialisation', test_env: typing.Dict[str, str],
returncode: int, duration: float, stdo: typing.Optional[str],
stde: typing.Optional[str],
returncode: int, starttime: float, duration: float,
stdo: typing.Optional[str], stde: typing.Optional[str],
cmd: typing.Optional[typing.List[str]]) -> 'TestRun':
if returncode == GNU_SKIP_RETURNCODE:
res = TestResult.SKIP
@ -331,11 +331,12 @@ class TestRun:
res = TestResult.EXPECTEDFAIL if bool(returncode) else TestResult.UNEXPECTEDPASS
else:
res = TestResult.FAIL if bool(returncode) else TestResult.OK
return cls(test, test_env, res, returncode, duration, stdo, stde, cmd)
return cls(test, test_env, res, returncode, starttime, duration, stdo, stde, cmd)
@classmethod
def make_tap(cls, test: 'TestSerialisation', test_env: typing.Dict[str, str],
returncode: int, duration: float, stdo: str, stde: str,
returncode: int, starttime: float, duration: float,
stdo: str, stde: str,
cmd: typing.Optional[typing.List[str]]) -> 'TestRun':
res = None
num_tests = 0
@ -369,15 +370,16 @@ class TestRun:
else:
res = TestResult.FAIL if failed else TestResult.OK
return cls(test, test_env, res, returncode, duration, stdo, stde, cmd)
return cls(test, test_env, res, returncode, starttime, duration, stdo, stde, cmd)
def __init__(self, test: 'TestSerialisation', test_env: typing.Dict[str, str],
res: TestResult, returncode: int, duration: float,
res: TestResult, returncode: int, starttime: float, duration: float,
stdo: typing.Optional[str], stde: typing.Optional[str],
cmd: typing.Optional[typing.List[str]]):
assert isinstance(res, TestResult)
self.res = res
self.returncode = returncode
self.starttime = starttime
self.duration = duration
self.stdo = stdo
self.stde = stde
@ -391,7 +393,10 @@ class TestRun:
res += 'NONE\n'
else:
test_only_env = set(self.env.items()) - set(os.environ.items())
res += '{}{}\n'.format(env_tuple_to_str(test_only_env), ' '.join(self.cmd))
starttime_str = time.strftime("%H:%M:%S", time.gmtime(self.starttime))
res += '{} {}{}\n'.format(
starttime_str, env_tuple_to_str(test_only_env), ' '.join(self.cmd)
)
if self.stdo:
res += '--- stdout ---\n'
res += self.stdo
@ -417,6 +422,7 @@ def write_json_log(jsonlogfile: typing.TextIO, test_name: str, result: TestRun)
jresult = {'name': test_name,
'stdout': result.stdo,
'result': result.res.value,
'starttime': result.starttime,
'duration': result.duration,
'returncode': result.returncode,
'env': result.env,
@ -480,7 +486,7 @@ class SingleTestRunner:
cmd = self._get_cmd()
if cmd is None:
skip_stdout = 'Not run because can not execute cross compiled binaries.'
return TestRun(self.test, self.test_env, TestResult.SKIP, GNU_SKIP_RETURNCODE, 0.0, skip_stdout, None, None)
return TestRun(self.test, self.test_env, TestResult.SKIP, GNU_SKIP_RETURNCODE, time.time(), 0.0, skip_stdout, None, None)
else:
wrap = TestHarness.get_wrapper(self.options)
if self.options.gdb:
@ -533,7 +539,7 @@ class SingleTestRunner:
# We don't want setsid() in gdb because gdb needs the
# terminal in order to handle ^C and not show tcsetpgrp()
# errors avoid not being able to use the terminal.
os.setsid()
os.setsid() # type: ignore
p = subprocess.Popen(cmd,
stdout=stdout,
@ -570,11 +576,11 @@ class SingleTestRunner:
# killing a process and all its children so we need
# to roll our own.
if is_windows():
subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
subprocess.run(['taskkill', '/F', '/T', '/PID', str(p.pid)])
else:
try:
# Kill the process group that setsid() created.
os.killpg(p.pid, signal.SIGKILL)
os.killpg(p.pid, signal.SIGKILL) # type: ignore
except ProcessLookupError:
# Sometimes (e.g. with Wine) this happens.
# There's nothing we can do (maybe the process
@ -610,14 +616,14 @@ class SingleTestRunner:
stdo = ""
stde = additional_error
if timed_out:
return TestRun(self.test, self.test_env, TestResult.TIMEOUT, p.returncode, duration, stdo, stde, cmd)
return TestRun(self.test, self.test_env, TestResult.TIMEOUT, p.returncode, starttime, duration, stdo, stde, cmd)
else:
if self.test.protocol == 'exitcode':
return TestRun.make_exitcode(self.test, self.test_env, p.returncode, duration, stdo, stde, cmd)
return TestRun.make_exitcode(self.test, self.test_env, p.returncode, starttime, duration, stdo, stde, cmd)
else:
if self.options.verbose:
print(stdo, end='')
return TestRun.make_tap(self.test, self.test_env, p.returncode, duration, stdo, stde, cmd)
return TestRun.make_tap(self.test, self.test_env, p.returncode, starttime, duration, stdo, stde, cmd)
class TestHarness:

@ -48,10 +48,10 @@ class WrapMode(Enum):
nodownload = 3
forcefallback = 4
def __str__(self):
def __str__(self) -> str:
return self.name
@staticmethod
def from_string(mode_name):
def from_string(mode_name: str):
g = string_to_value[mode_name]
return WrapMode(g)

@ -14,14 +14,24 @@
from .. import mlog
import contextlib
import urllib.request, os, hashlib, shutil, tempfile, stat
import urllib.request
import urllib.error
import os
import hashlib
import shutil
import tempfile
import stat
import subprocess
import sys
import configparser
import typing
from . import WrapMode
from ..mesonlib import ProgressBar, MesonException
if typing.TYPE_CHECKING:
import http.client
try:
import ssl
has_ssl = True
@ -33,7 +43,7 @@ except ImportError:
req_timeout = 600.0
ssl_warning_printed = False
def build_ssl_context():
def build_ssl_context() -> 'ssl.SSLContext':
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.options |= ssl.OP_NO_SSLv2
ctx.options |= ssl.OP_NO_SSLv3
@ -41,35 +51,37 @@ def build_ssl_context():
ctx.load_default_certs()
return ctx
def quiet_git(cmd, workingdir):
def quiet_git(cmd: typing.List[str], workingdir: str) -> typing.Tuple[bool, str]:
try:
pc = subprocess.Popen(['git', '-C', workingdir] + cmd, stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pc = subprocess.run(['git', '-C', workingdir] + cmd, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError as e:
return False, str(e)
out, err = pc.communicate()
if pc.returncode != 0:
return False, err
return True, out
return False, pc.stderr
return True, pc.stdout
def open_wrapdburl(urlstring):
def open_wrapdburl(urlstring: str) -> 'http.client.HTTPResponse':
global ssl_warning_printed
if has_ssl:
try:
return urllib.request.urlopen(urlstring, timeout=req_timeout)# , context=build_ssl_context())
except urllib.error.URLError:
if not ssl_warning_printed:
print('SSL connection failed. Falling back to unencrypted connections.')
print('SSL connection failed. Falling back to unencrypted connections.', file=sys.stderr)
ssl_warning_printed = True
if not ssl_warning_printed:
print('Warning: SSL not available, traffic not authenticated.',
file=sys.stderr)
print('Warning: SSL not available, traffic not authenticated.', file=sys.stderr)
ssl_warning_printed = True
# Trying to open SSL connection to wrapdb fails because the
# certificate is not known.
if urlstring.startswith('https'):
urlstring = 'http' + urlstring[5:]
return urllib.request.urlopen(urlstring, timeout=req_timeout)
try:
return urllib.request.urlopen(urlstring, timeout=req_timeout)
except urllib.error.URLError:
raise WrapException('failed to get {} is the internet available?'.format(urlstring))
class WrapException(MesonException):
pass
@ -78,7 +90,7 @@ class WrapNotFoundException(WrapException):
pass
class PackageDefinition:
def __init__(self, fname):
def __init__(self, fname: str):
self.filename = fname
self.basename = os.path.basename(fname)
self.name = self.basename[:-5]
@ -96,23 +108,23 @@ class PackageDefinition:
self.type = self.wrap_section[5:]
self.values = dict(self.config[self.wrap_section])
def get(self, key):
def get(self, key: str) -> str:
try:
return self.values[key]
except KeyError:
m = 'Missing key {!r} in {}'
raise WrapException(m.format(key, self.basename))
def has_patch(self):
def has_patch(self) -> bool:
return 'patch_url' in self.values
class Resolver:
def __init__(self, subdir_root, wrap_mode=WrapMode.default):
def __init__(self, subdir_root: str, wrap_mode=WrapMode.default):
self.wrap_mode = wrap_mode
self.subdir_root = subdir_root
self.cachedir = os.path.join(self.subdir_root, 'packagecache')
def resolve(self, packagename: str, method: str):
def resolve(self, packagename: str, method: str) -> str:
self.packagename = packagename
self.directory = packagename
# We always have to load the wrap file, if it exists, because it could
@ -168,20 +180,20 @@ class Resolver:
return self.directory
def load_wrap(self):
def load_wrap(self) -> PackageDefinition:
fname = os.path.join(self.subdir_root, self.packagename + '.wrap')
if os.path.isfile(fname):
return PackageDefinition(fname)
return None
def check_can_download(self):
def check_can_download(self) -> None:
# Don't download subproject data based on wrap file if requested.
# Git submodules are ok (see above)!
if self.wrap_mode is WrapMode.nodownload:
m = 'Automatic wrap-based subproject downloading is disabled'
raise WrapException(m)
def resolve_git_submodule(self):
def resolve_git_submodule(self) -> bool:
# Are we in a git repository?
ret, out = quiet_git(['rev-parse'], self.subdir_root)
if not ret:
@ -191,29 +203,29 @@ class Resolver:
if not ret:
return False
# Submodule has not been added, add it
if out.startswith(b'+'):
if out.startswith('+'):
mlog.warning('git submodule might be out of date')
return True
elif out.startswith(b'U'):
elif out.startswith('U'):
raise WrapException('git submodule has merge conflicts')
# Submodule exists, but is deinitialized or wasn't initialized
elif out.startswith(b'-'):
elif out.startswith('-'):
if subprocess.call(['git', '-C', self.subdir_root, 'submodule', 'update', '--init', self.dirname]) == 0:
return True
raise WrapException('git submodule failed to init')
# Submodule looks fine, but maybe it wasn't populated properly. Do a checkout.
elif out.startswith(b' '):
elif out.startswith(' '):
subprocess.call(['git', 'checkout', '.'], cwd=self.dirname)
# Even if checkout failed, try building it anyway and let the user
# handle any problems manually.
return True
elif out == b'':
elif out == '':
# It is not a submodule, just a folder that exists in the main repository.
return False
m = 'Unknown git submodule output: {!r}'
raise WrapException(m.format(out))
def get_file(self):
def get_file(self) -> None:
path = self.get_file_internal('source')
extract_dir = self.subdir_root
# Some upstreams ship packages that do not have a leading directory.
@ -225,7 +237,7 @@ class Resolver:
if self.wrap.has_patch():
self.apply_patch()
def get_git(self):
def get_git(self) -> None:
revno = self.wrap.get('revision')
is_shallow = self.wrap.values.get('depth', '') != ''
# for some reason git only allows commit ids to be shallowly fetched by fetch not with clone
@ -271,13 +283,13 @@ class Resolver:
'--push', 'origin', push_url],
cwd=self.dirname)
def is_git_full_commit_id(self, revno):
def is_git_full_commit_id(self, revno: str) -> bool:
result = False
if len(revno) in (40, 64): # 40 for sha1, 64 for upcoming sha256
result = all((ch in '0123456789AaBbCcDdEeFf' for ch in revno))
return result
def get_hg(self):
def get_hg(self) -> None:
revno = self.wrap.get('revision')
subprocess.check_call(['hg', 'clone', self.wrap.get('url'),
self.directory], cwd=self.subdir_root)
@ -285,19 +297,22 @@ class Resolver:
subprocess.check_call(['hg', 'checkout', revno],
cwd=self.dirname)
def get_svn(self):
def get_svn(self) -> None:
revno = self.wrap.get('revision')
subprocess.check_call(['svn', 'checkout', '-r', revno, self.wrap.get('url'),
self.directory], cwd=self.subdir_root)
def get_data(self, url):
def get_data(self, url: str) -> typing.Tuple[str, str]:
blocksize = 10 * 1024
h = hashlib.sha256()
tmpfile = tempfile.NamedTemporaryFile(mode='wb', dir=self.cachedir, delete=False)
if url.startswith('https://wrapdb.mesonbuild.com'):
resp = open_wrapdburl(url)
else:
resp = urllib.request.urlopen(url, timeout=req_timeout)
try:
resp = urllib.request.urlopen(url, timeout=req_timeout)
except urllib.error.URLError:
raise WrapException('could not get {} is the internet available?'.format(url))
with contextlib.closing(resp) as resp:
try:
dlsize = int(resp.info()['Content-Length'])
@ -327,7 +342,7 @@ class Resolver:
hashvalue = h.hexdigest()
return hashvalue, tmpfile.name
def check_hash(self, what, path):
def check_hash(self, what: str, path: str) -> None:
expected = self.wrap.get(what + '_hash')
h = hashlib.sha256()
with open(path, 'rb') as f:
@ -336,7 +351,7 @@ class Resolver:
if dhash != expected:
raise WrapException('Incorrect hash for %s:\n %s expected\n %s actual.' % (what, expected, dhash))
def download(self, what, ofname):
def download(self, what: str, ofname: str) -> None:
self.check_can_download()
srcurl = self.wrap.get(what + '_url')
mlog.log('Downloading', mlog.bold(self.packagename), what, 'from', mlog.bold(srcurl))
@ -347,7 +362,7 @@ class Resolver:
raise WrapException('Incorrect hash for %s:\n %s expected\n %s actual.' % (what, expected, dhash))
os.rename(tmpfile, ofname)
def get_file_internal(self, what):
def get_file_internal(self, what: str) -> str:
filename = self.wrap.get(what + '_filename')
cache_path = os.path.join(self.cachedir, filename)
@ -361,7 +376,7 @@ class Resolver:
self.download(what, cache_path)
return cache_path
def apply_patch(self):
def apply_patch(self) -> None:
path = self.get_file_internal('patch')
try:
shutil.unpack_archive(path, self.subdir_root)
@ -370,7 +385,7 @@ class Resolver:
shutil.unpack_archive(path, workdir)
self.copy_tree(workdir, self.subdir_root)
def copy_tree(self, root_src_dir, root_dst_dir):
def copy_tree(self, root_src_dir: str, root_dst_dir: str) -> None:
"""
Copy directory tree. Overwrites also read only files.
"""

@ -58,9 +58,8 @@ def get_result(urlstring):
data = u.read().decode('utf-8')
jd = json.loads(data)
if jd['output'] != 'ok':
print('Got bad output from server.')
print(data)
sys.exit(1)
print('Got bad output from server.', file=sys.stderr)
raise SystemExit(data)
return jd
def get_projectlist():
@ -79,7 +78,7 @@ def search(options):
for p in jd['projects']:
print(p)
def get_latest_version(name):
def get_latest_version(name: str) -> tuple:
jd = get_result(API_ROOT + 'query/get_latest/' + name)
branch = jd['branch']
revision = jd['revision']
@ -88,15 +87,12 @@ def get_latest_version(name):
def install(options):
name = options.name
if not os.path.isdir('subprojects'):
print('Subprojects dir not found. Run this script in your source root directory.')
sys.exit(1)
raise SystemExit('Subprojects dir not found. Run this script in your source root directory.')
if os.path.isdir(os.path.join('subprojects', name)):
print('Subproject directory for this project already exists.')
sys.exit(1)
raise SystemExit('Subproject directory for this project already exists.')
wrapfile = os.path.join('subprojects', name + '.wrap')
if os.path.exists(wrapfile):
print('Wrap file already exists.')
sys.exit(1)
raise SystemExit('Wrap file already exists.')
(branch, revision) = get_latest_version(name)
u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%s/get_wrap' % (name, branch, revision))
data = u.read()
@ -125,17 +121,15 @@ def update_wrap_file(wrapfile, name, new_branch, new_revision):
def update(options):
name = options.name
if not os.path.isdir('subprojects'):
print('Subprojects dir not found. Run this command in your source root directory.')
sys.exit(1)
raise SystemExit('Subprojects dir not found. Run this command in your source root directory.')
wrapfile = os.path.join('subprojects', name + '.wrap')
if not os.path.exists(wrapfile):
print('Project', name, 'is not in use.')
sys.exit(1)
raise SystemExit('Project ' + name + ' is not in use.')
(branch, revision, subdir, src_file, patch_file) = get_current_version(wrapfile)
(new_branch, new_revision) = get_latest_version(name)
if new_branch == branch and new_revision == revision:
print('Project', name, 'is already up to date.')
sys.exit(0)
print('Project ' + name + ' is already up to date.')
raise SystemExit
update_wrap_file(wrapfile, name, new_branch, new_revision)
shutil.rmtree(os.path.join('subprojects', subdir), ignore_errors=True)
try:
@ -153,8 +147,7 @@ def info(options):
jd = get_result(API_ROOT + 'projects/' + name)
versions = jd['versions']
if not versions:
print('No available versions of', name)
sys.exit(0)
raise SystemExit('No available versions of' + name)
print('Available versions of %s:' % name)
for v in versions:
print(' ', v['branch'], v['revision'])
@ -167,7 +160,7 @@ def do_promotion(from_path, spdir_name):
sproj_name = os.path.basename(from_path)
outputdir = os.path.join(spdir_name, sproj_name)
if os.path.exists(outputdir):
sys.exit('Output dir %s already exists. Will not overwrite.' % outputdir)
raise SystemExit('Output dir %s already exists. Will not overwrite.' % outputdir)
shutil.copytree(from_path, outputdir, ignore=shutil.ignore_patterns('subprojects'))
def promote(options):
@ -184,13 +177,13 @@ def promote(options):
# otherwise the argument is just a subproject basename which must be unambiguous
if argument not in sprojs:
sys.exit('Subproject %s not found in directory tree.' % argument)
raise SystemExit('Subproject %s not found in directory tree.' % argument)
matches = sprojs[argument]
if len(matches) > 1:
print('There is more than one version of %s in tree. Please specify which one to promote:\n' % argument)
print('There is more than one version of %s in tree. Please specify which one to promote:\n' % argument, file=sys.stderr)
for s in matches:
print(s)
sys.exit(1)
print(s, file=sys.stderr)
raise SystemExit(1)
do_promotion(matches[0], spdir_name)
def status(options):
@ -200,12 +193,12 @@ def status(options):
try:
(latest_branch, latest_revision) = get_latest_version(name)
except Exception:
print('', name, 'not available in wrapdb.')
print('', name, 'not available in wrapdb.', file=sys.stderr)
continue
try:
(current_branch, current_revision, _, _, _) = get_current_version(w)
except Exception:
print('Wrap file not from wrapdb.')
print('Wrap file not from wrapdb.', file=sys.stderr)
continue
if current_branch == latest_branch and current_revision == latest_revision:
print('', name, 'up to date. Branch %s, revision %d.' % (current_branch, current_revision))

@ -0,0 +1,21 @@
project('fs module test')
fs = import('fs')
assert(fs.exists('meson.build'), 'Existing file reported as missing.')
assert(not fs.exists('nonexisting'), 'Nonexisting file was found.')
if build_machine.system() != 'windows' and build_machine.system() != 'cygwin'
assert(fs.is_symlink('a_symlink'), 'Symlink not detected.')
assert(not fs.is_symlink('meson.build'), 'Regular file detected as symlink.')
endif
assert(fs.is_file('meson.build'), 'File not detected as a file.')
assert(not fs.is_file('subprojects'), 'Directory detected as a file.')
assert(not fs.is_file('nonexisting'), 'Bad path detected as a file.')
assert(fs.is_dir('subprojects'), 'Dir not detected correctly.')
assert(not fs.is_dir('meson.build'), 'File detected as a dir.')
assert(not fs.is_dir('nonexisting'), 'Bad path detected as a dir.')
subdir('subdir')

@ -0,0 +1 @@
assert(fs.exists('subdirfile.txt'), 'Subdir file lookup is broken.')

@ -0,0 +1,9 @@
project('subbie')
fs = import('fs')
assert(fs.exists('subprojectfile.txt'), 'Subproject root file not found.')
subdir('subsub')
subproject('subbie')

@ -0,0 +1 @@
assert(fs.exists('subsubfile.txt'), 'Subproject subdir lookup failed.')

@ -3,7 +3,7 @@
#include<string.h>
#include<stdio.h>
int main(int argc, char **argv) {
int main(void) {
if(strcmp("bob", get_bob()) == 0) {
printf("Bob is indeed bob.\n");
} else {

@ -389,9 +389,9 @@ with open(sys.argv[1]) as f:
token = arr[1]
if token in function_data:
fdata = function_data[token]
functions.append((token, fdata[0], fdata[1]))
functions.append([token, fdata[0], fdata[1]])
elif token.startswith('HAVE_') and not token.endswith('_H'):
functions.append((token, ))
functions.append([token])
except Exception:
pass
@ -427,12 +427,12 @@ endforeach
# Convert function checks.
print('check_functions = [')
for token in functions:
if len(token) == 3:
token, fdata0, fdata1 = token
print(" ['%s', '%s', '#include<%s>']," % (token, fdata0, fdata1))
for tok in functions:
if len(tok) == 3:
tokstr, fdata0, fdata1 = tok
print(" ['%s', '%s', '#include<%s>']," % (tokstr, fdata0, fdata1))
else:
print('# check token', token)
print('# check token', tok)
print(']\n')
print('''foreach f : check_functions

@ -31,7 +31,7 @@ import json
import re
Module = collections.namedtuple('Module', ['dirname', 'name', 'libnames'])
Module.__repr__ = lambda self: str((self.dirname, self.name, self.libnames))
Module.__repr__ = lambda self: str((self.dirname, self.name, self.libnames)) # type: ignore
LIBS = 'libs'

@ -32,25 +32,28 @@ to this:
This directory must be run from source root as it touches run_unittests.py.
'''
import os, sys, subprocess
import typing
import os
import sys
import subprocess
from glob import glob
def get_entries():
def get_entries() -> typing.List[typing.Tuple[int, str]]:
entries = []
for e in glob('*'):
if not os.path.isdir(e):
sys.exit('Current directory must not contain any files.')
raise SystemExit('Current directory must not contain any files.')
(number, rest) = e.split(' ', 1)
try:
number = int(number)
numstr = int(number)
except ValueError:
sys.exit('Dir name %d does not start with a number.' % e)
entries.append((number, rest))
raise SystemExit('Dir name {} does not start with a number.'.format(e))
entries.append((numstr, rest))
entries.sort()
return entries
def replace_source(sourcefile, replacements):
def replace_source(sourcefile: str, replacements: typing.List[typing.Tuple[str, str]]):
with open(sourcefile, 'r') as f:
contents = f.read()
for old_name, new_name in replacements:
@ -58,7 +61,7 @@ def replace_source(sourcefile, replacements):
with open(sourcefile, 'w') as f:
f.write(contents)
def condense(dirname):
def condense(dirname: str):
curdir = os.getcwd()
os.chdir(dirname)
entries = get_entries()
@ -77,6 +80,6 @@ def condense(dirname):
if __name__ == '__main__':
if len(sys.argv) != 1:
sys.exit('This script takes no arguments.')
raise SystemExit('This script takes no arguments.')
for d in glob('test cases/*'):
condense(d)

Loading…
Cancel
Save