Add meson.build files to build with meson

Provide both build systems in parallel for now, to ensure that we work
out all the differences between the two. Some time from now, we'll give
up on autotools.

Meson tends to be faster and probably easier to read/maintain. On my
machine, the full meson configure+build+install takes a little under
half as long as a similar autotools-based invocation.

Building with meson is a two step process. First, configure the build:

  meson build

Then, compile the project:

  ninja -C build

There's some mild differences in functionality between meson and
autotools.  specifically:

1) No singular update-po target. meson only generates individual
update-po targets for each textdomain (of which we have 3).  To make
this easier, there's a build-aux/update-po script which finds all
update-po targets and runs them.

2) No 'make dist' equivalent. Just run 'git archive' to generate a
suitable tarball for distribution.
This commit is contained in:
Dave Reisner 2018-07-07 10:29:48 -04:00
parent dab45f0808
commit 51db84750e
27 changed files with 2063 additions and 0 deletions

250
.ycm_extra_conf.py Normal file
View file

@ -0,0 +1,250 @@
#!/usr/bin/env python
# SPDX-License-Identifier: Unlicense
#
# Based on the template file provided by the 'YCM-Generator' project authored by
# Reuben D'Netto.
# Jiahui Xie has re-reformatted and expanded the original script in accordance
# to the requirements of the PEP 8 style guide and 'systemd' project,
# respectively.
#
# The original license is preserved as it is.
#
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
"""
YouCompleteMe configuration file tailored to support the 'meson' build system
used by the 'systemd' project.
"""
import glob
import os
import ycm_core
SOURCE_EXTENSIONS = (".C", ".cpp", ".cxx", ".cc", ".c", ".m", ".mm")
HEADER_EXTENSIONS = (".H", ".h", ".hxx", ".hpp", ".hh")
def DirectoryOfThisScript():
"""
Return the absolute path of the parent directory containing this
script.
"""
return os.path.dirname(os.path.abspath(__file__))
def GuessBuildDirectory():
"""
Guess the build directory using the following heuristics:
1. Returns the current directory of this script plus 'build'
subdirectory in absolute path if this subdirectory exists.
2. Otherwise, probes whether there exists any directory
containing '.ninja_log' file two levels above the current directory;
returns this single directory only if there is one candidate.
"""
result = os.path.join(DirectoryOfThisScript(), "build")
if os.path.exists(result):
return result
result = glob.glob(os.path.join(DirectoryOfThisScript(),
"..", "..", "*", ".ninja_log"))
if not result:
return ""
if 1 != len(result):
return ""
return os.path.split(result[0])[0]
def TraverseByDepth(root, include_extensions):
"""
Return a set of child directories of the 'root' containing file
extensions specified in 'include_extensions'.
NOTE:
1. The 'root' directory itself is excluded from the result set.
2. No subdirectories would be excluded if 'include_extensions' is left
to 'None'.
3. Each entry in 'include_extensions' must begin with string '.'.
"""
is_root = True
result = set()
# Perform a depth first top down traverse of the given directory tree.
for root_dir, subdirs, file_list in os.walk(root):
if not is_root:
# print("Relative Root: ", root_dir)
# print(subdirs)
if include_extensions:
get_ext = os.path.splitext
subdir_extensions = {
get_ext(f)[-1] for f in file_list if get_ext(f)[-1]
}
if subdir_extensions & include_extensions:
result.add(root_dir)
else:
result.add(root_dir)
else:
is_root = False
return result
_project_src_dir = os.path.join(DirectoryOfThisScript(), "src")
_include_dirs_set = TraverseByDepth(_project_src_dir, frozenset({".h"}))
flags = [
"-x",
"c"
# The following flags are partially redundant due to the existence of
# 'compile_commands.json'.
# '-Wall',
# '-Wextra',
# '-Wfloat-equal',
# '-Wpointer-arith',
# '-Wshadow',
# '-std=gnu99',
]
for include_dir in _include_dirs_set:
flags.append("-I" + include_dir)
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = GuessBuildDirectory()
if os.path.exists(compilation_database_folder):
database = ycm_core.CompilationDatabase(compilation_database_folder)
else:
database = None
def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
"""
Iterate through 'flags' and replace the relative paths prefixed by
'-isystem', '-I', '-iquote', '--sysroot=' with absolute paths
start with 'working_directory'.
"""
if not working_directory:
return list(flags)
new_flags = []
make_next_absolute = False
path_flags = ["-isystem", "-I", "-iquote", "--sysroot="]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith("/"):
new_flag = os.path.join(working_directory, flag)
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith(path_flag):
path = flag[len(path_flag):]
new_flag = path_flag + os.path.join(working_directory, path)
break
if new_flag:
new_flags.append(new_flag)
return new_flags
def IsHeaderFile(filename):
"""
Check whether 'filename' is considered as a header file.
"""
extension = os.path.splitext(filename)[1]
return extension in HEADER_EXTENSIONS
def GetCompilationInfoForFile(filename):
"""
Helper function to look up compilation info of 'filename' in the 'database'.
"""
# The compilation_commands.json file generated by CMake does not have
# entries for header files. So we do our best by asking the db for flags for
# a corresponding source file, if any. If one exists, the flags for that
# file should be good enough.
if not database:
return None
if IsHeaderFile(filename):
basename = os.path.splitext(filename)[0]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists(replacement_file):
compilation_info = \
database.GetCompilationInfoForFile(replacement_file)
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile(filename)
def FlagsForFile(filename, **kwargs):
"""
Callback function to be invoked by YouCompleteMe in order to get the
information necessary to compile 'filename'.
It returns a dictionary with a single element 'flags'. This element is a
list of compiler flags to pass to libclang for the file 'filename'.
"""
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile(filename)
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_)
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
return {
"flags": final_flags,
"do_cache": True
}

View file

@ -0,0 +1,33 @@
#!@BASH@
input=$1
output=$2
mode=$3
"@SED@" \
-e "s|@rootdir[@]|@ROOTDIR@|g" \
-e "s|@localedir[@]|@LOCALEDIR@|g" \
-e "s|@sysconfdir[@]|@sysconfdir@|g" \
-e "s|@localstatedir[@]|@localstatedir@|g" \
-e "s|@libmakepkgdir[@]|@LIBMAKEPKGDIR@|g" \
-e "s|@pkgdatadir[@]|@PKGDATADIR@|g" \
-e "s|@prefix[@]|@PREFIX@|g" \
-e "1s|#!/bin/bash|#!@BASH@|g" \
-e "s|@PACKAGE_VERSION[@]|@PACKAGE_VERSION@|g" \
-e "s|@PACKAGE_NAME[@]|@PACKAGE_NAME@|g" \
-e "s|@BUILDSCRIPT[@]|@BUILDSCRIPT@|g" \
-e "s|@TEMPLATE_DIR[@]|@TEMPLATE_DIR@|g" \
-e "s|@DEBUGSUFFIX[@]|@DEBUGSUFFIX@|g" \
-e "s|@INODECMD[@]|@INODECMD@|g" \
-e "s|@OWNERCMD[@]|@OWNERCMD@|g" \
-e "s|@MODECMD[@]|@MODECMD@|g" \
-e "s|@SEDINPLACEFLAGS[@]|@SEDINPLACEFLAGS@|g" \
-e "s|@SEDPATH[@]|@SEDPATH@|g" \
-e "s|@DUFLAGS[@]|@DUFLAGS@|g" \
-e "s|@DUPATH[@]|@DUPATH@|g" \
-e "s|@configure_input[@]|Generated from ${output##*/}.sh.in; do not edit by hand.|g" \
"$input" >"$output"
if [[ $mode ]]; then
chmod "$mode" "$output"
fi

View file

@ -0,0 +1,12 @@
#!/bin/sh
set -eu
# this is needed mostly because $DESTDIR is provided as a variable,
# and we need to create the target directory...
mkdir -vp "$(dirname "${DESTDIR:-}$2")"
if [ "$(dirname $1)" = . ]; then
ln -vfs -T "$1" "${DESTDIR:-}$2"
else
ln -vfs -T --relative "${DESTDIR:-}$1" "${DESTDIR:-}$2"
fi

296
build-aux/tap-driver.py Normal file
View file

@ -0,0 +1,296 @@
#!/usr/bin/env python3
# Adapted from tappy copyright (c) 2016, Matt Layman
# MIT license
# https://github.com/python-tap/tappy
import io
import re
import subprocess
import sys
class Directive(object):
"""A representation of a result line directive."""
skip_pattern = re.compile(
r"""^SKIP\S*
(?P<whitespace>\s*) # Optional whitespace.
(?P<reason>.*) # Slurp up the rest.""",
re.IGNORECASE | re.VERBOSE)
todo_pattern = re.compile(
r"""^TODO\b # The directive name
(?P<whitespace>\s*) # Immediately following must be whitespace.
(?P<reason>.*) # Slurp up the rest.""",
re.IGNORECASE | re.VERBOSE)
def __init__(self, text):
"""Initialize the directive by parsing the text.
The text is assumed to be everything after a '#\s*' on a result line.
"""
self._text = text
self._skip = False
self._todo = False
self._reason = None
match = self.skip_pattern.match(text)
if match:
self._skip = True
self._reason = match.group('reason')
match = self.todo_pattern.match(text)
if match:
if match.group('whitespace'):
self._todo = True
else:
# Catch the case where the directive has no descriptive text.
if match.group('reason') == '':
self._todo = True
self._reason = match.group('reason')
@property
def text(self):
"""Get the entire text."""
return self._text
@property
def skip(self):
"""Check if the directive is a SKIP type."""
return self._skip
@property
def todo(self):
"""Check if the directive is a TODO type."""
return self._todo
@property
def reason(self):
"""Get the reason for the directive."""
return self._reason
class Parser(object):
"""A parser for TAP files and lines."""
# ok and not ok share most of the same characteristics.
result_base = r"""
\s* # Optional whitespace.
(?P<number>\d*) # Optional test number.
\s* # Optional whitespace.
(?P<description>[^#]*) # Optional description before #.
\#? # Optional directive marker.
\s* # Optional whitespace.
(?P<directive>.*) # Optional directive text.
"""
ok = re.compile(r'^ok' + result_base, re.VERBOSE)
not_ok = re.compile(r'^not\ ok' + result_base, re.VERBOSE)
plan = re.compile(r"""
^1..(?P<expected>\d+) # Match the plan details.
[^#]* # Consume any non-hash character to confirm only
# directives appear with the plan details.
\#? # Optional directive marker.
\s* # Optional whitespace.
(?P<directive>.*) # Optional directive text.
""", re.VERBOSE)
diagnostic = re.compile(r'^#')
bail = re.compile(r"""
^Bail\ out!
\s* # Optional whitespace.
(?P<reason>.*) # Optional reason.
""", re.VERBOSE)
version = re.compile(r'^TAP version (?P<version>\d+)$')
TAP_MINIMUM_DECLARED_VERSION = 13
def parse(self, fh):
"""Generate tap.line.Line objects, given a file-like object `fh`.
`fh` may be any object that implements both the iterator and
context management protocol (i.e. it can be used in both a
"with" statement and a "for...in" statement.)
Trailing whitespace and newline characters will be automatically
stripped from the input lines.
"""
with fh:
for line in fh:
yield self.parse_line(line.rstrip())
def parse_line(self, text):
"""Parse a line into whatever TAP category it belongs."""
match = self.ok.match(text)
if match:
return self._parse_result(True, match)
match = self.not_ok.match(text)
if match:
return self._parse_result(False, match)
if self.diagnostic.match(text):
return ('diagnostic', text)
match = self.plan.match(text)
if match:
return self._parse_plan(match)
match = self.bail.match(text)
if match:
return ('bail', match.group('reason'))
match = self.version.match(text)
if match:
return self._parse_version(match)
return ('unknown',)
def _parse_plan(self, match):
"""Parse a matching plan line."""
expected_tests = int(match.group('expected'))
directive = Directive(match.group('directive'))
# Only SKIP directives are allowed in the plan.
if directive.text and not directive.skip:
return ('unknown',)
return ('plan', expected_tests, directive)
def _parse_result(self, ok, match):
"""Parse a matching result line into a result instance."""
return ('result', ok, match.group('number'),
match.group('description').strip(),
Directive(match.group('directive')))
def _parse_version(self, match):
version = int(match.group('version'))
if version < self.TAP_MINIMUM_DECLARED_VERSION:
raise ValueError('It is an error to explicitly specify '
'any version lower than 13.')
return ('version', version)
class Rules(object):
def __init__(self):
self._lines_seen = {'plan': [], 'test': 0, 'failed': 0, 'version': []}
self._errors = []
def check(self, final_line_count):
"""Check the status of all provided data and update the suite."""
if self._lines_seen['version']:
self._process_version_lines()
self._process_plan_lines(final_line_count)
def check_errors(self):
if self._lines_seen['failed'] > 0:
self._add_error('Tests failed.')
if self._errors:
for error in self._errors:
print(error)
return 1
return 0
def _process_version_lines(self):
"""Process version line rules."""
if len(self._lines_seen['version']) > 1:
self._add_error('Multiple version lines appeared.')
elif self._lines_seen['version'][0] != 1:
self._add_error('The version must be on the first line.')
def _process_plan_lines(self, final_line_count):
"""Process plan line rules."""
if not self._lines_seen['plan']:
self._add_error('Missing a plan.')
return
if len(self._lines_seen['plan']) > 1:
self._add_error('Only one plan line is permitted per file.')
return
expected_tests, at_line = self._lines_seen['plan'][0]
if not self._plan_on_valid_line(at_line, final_line_count):
self._add_error(
'A plan must appear at the beginning or end of the file.')
return
if expected_tests != self._lines_seen['test']:
self._add_error(
'Expected {expected_count} tests '
'but only {seen_count} ran.'.format(
expected_count=expected_tests,
seen_count=self._lines_seen['test']))
def _plan_on_valid_line(self, at_line, final_line_count):
"""Check if a plan is on a valid line."""
# Put the common cases first.
if at_line == 1 or at_line == final_line_count:
return True
# The plan may only appear on line 2 if the version is at line 1.
after_version = (
self._lines_seen['version'] and
self._lines_seen['version'][0] == 1 and
at_line == 2)
if after_version:
return True
return False
def handle_bail(self, reason):
"""Handle a bail line."""
self._add_error('Bailed: {reason}').format(reason=reason)
def handle_skipping_plan(self):
"""Handle a plan that contains a SKIP directive."""
sys.exit(77)
def saw_plan(self, expected_tests, at_line):
"""Record when a plan line was seen."""
self._lines_seen['plan'].append((expected_tests, at_line))
def saw_test(self, ok):
"""Record when a test line was seen."""
self._lines_seen['test'] += 1
if not ok:
self._lines_seen['failed'] += 1
def saw_version_at(self, line_counter):
"""Record when a version line was seen."""
self._lines_seen['version'].append(line_counter)
def _add_error(self, message):
self._errors += [message]
if __name__ == '__main__':
parser = Parser()
rules = Rules()
try:
out = subprocess.check_output(sys.argv[1:], universal_newlines=True)
except subprocess.CalledProcessError as e:
sys.stdout.write(e.output)
raise e
line_generator = parser.parse(io.StringIO(out))
line_counter = 0
for line in line_generator:
line_counter += 1
if line[0] == 'unknown':
continue
if line[0] == 'result':
rules.saw_test(line[1])
print('{okay} {num} {description} {directive}'.format(
okay=('' if line[1] else 'not ') + 'ok', num=line[2],
description=line[3], directive=line[4].text))
elif line[0] == 'plan':
if line[2].skip:
rules.handle_skipping_plan()
rules.saw_plan(line[1], line_counter)
elif line[0] == 'bail':
rules.handle_bail(line[1])
elif line[0] == 'version':
rules.saw_version_at(line_counter)
elif line[0] == 'diagnostic':
print(line[1])
rules.check(line_counter)
sys.exit(rules.check_errors())

39
build-aux/update-po Executable file
View file

@ -0,0 +1,39 @@
#!/bin/bash
find_build_directory() {
local build_dirs=(*/.ninja_log)
if [[ ! -e ${build_dirs[0]} ]]; then
echo "error: No build directory found. Have you run 'meson build' yet?" >&2
return 1
elif (( ${#build_dirs[*]} > 1 )); then
echo "error: Multiple build directories found. Unable to proceed." >&2
return 1
fi
printf '%s\n' "${build_dirs[0]%/*}"
}
filter_targets_by_name() {
if command -v jq &>/dev/null; then
jq --arg re "$1" -r 'map(.name)[] | select(match($re))'
else
json_pp | awk -v filter="$1" -F'[:"]' \
'$2 == "name" && $(NF - 1) ~ filter { print $(NF - 1) }'
fi
}
# Make things simple and require that we're in the build root rather than
# trying to chase down the location of this script and the relative build dir.
if [[ ! -d .git ]]; then
echo "This script must be run from the root of the repository" >&2
exit 1
fi
build_dir=$(find_build_directory) || exit 1
mapfile -t targets < \
<(meson introspect "$build_dir" --targets | filter_targets_by_name "-update-po$")
ninja -C "$build_dir" "${targets[@]}"

138
doc/meson.build Normal file
View file

@ -0,0 +1,138 @@
manpages = [
{ 'name': 'alpm-hooks.5' },
{ 'name': 'pacman.8' },
{ 'name': 'makepkg.8' },
{ 'name': 'makepkg-template.1' },
{ 'name': 'repo-add.8' },
{ 'name': 'vercmp.8' },
{ 'name': 'pkgdelta.8' },
{ 'name': 'pacman-key.8' },
{ 'name': 'PKGBUILD.5', 'extra_depends' : [ 'PKGBUILD-example.txt' ] },
{ 'name': 'makepkg.conf.5' },
{ 'name': 'pacman.conf.5' },
{ 'name': 'libalpm.3' },
{ 'name': 'BUILDINFO.5' },
]
asciidoc_conf = join_paths(meson.current_source_dir(), 'asciidoc.conf')
asciidoc_opts = [
'-f', asciidoc_conf,
'-a', 'pacman_version="@0@"'.format(PACKAGE_VERSION),
'-a', 'pacman_date=@0@'.format(run_command('date', '+%Y-%m-%d').stdout().strip()),
'-a', 'pkgdatadir=@0@'.format(PKGDATADIR),
'-a', 'localstatedir=@0@'.format(LOCALSTATEDIR),
'-a', 'sysconfdir=@0@'.format(SYSCONFDIR),
'-a', 'datarootdir=@0@'.format(DATAROOTDIR),
]
html_targets = []
html_files = []
foreach page : manpages
manpage = page['name']
htmlpage = '@0@.html'.format(manpage)
input = '@0@.asciidoc'.format(manpage)
section = page['name'].split('.')[-1]
mandirn = join_paths(MANDIR, 'man' + section)
custom_target(
manpage,
command : [
A2X,
'--no-xmllint',
'-d', 'manpage',
'-f', 'manpage',
'--xsltproc-opts', '-param man.endnotes.list.enabled 0 -param man.endnotes.are.numbered 0',
'-D', '@OUTDIR@',
'--asciidoc-opts', ' '.join(asciidoc_opts),
'@INPUT@',
],
input : input,
output : [manpage],
depend_files : [
asciidoc_conf,
] + page.get('extra_depends', []),
install : true,
install_dir : mandirn,
)
html = custom_target(
htmlpage,
command : [
ASCIIDOC,
] + asciidoc_opts + [
'-a', 'linkcss',
'-a', 'toc',
'-a', 'icons',
'-a', 'max-width=960px',
'-a', 'stylesheet=asciidoc-override.css',
'-o', '@OUTPUT@',
'@INPUT@',
],
input : input,
output : [htmlpage],
depend_files : [
asciidoc_conf,
'asciidoc-override.css',
] + page.get('extra_depends', []),
build_by_default : false,
install : false,
)
html_targets += [html]
html_files += [htmlpage]
endforeach
run_target('html',
command : ['/bin/true'],
depends : html_targets)
custom_target(
'website.tar.gz',
command : [
'bsdtar', 'czf', '@OUTPUT@',
'-C', meson.current_build_dir(),
] + html_files + [
'-C', meson.current_source_dir(),
'submitting-patches.html',
'translation-help.html',
'HACKING.html',
'index.html',
'asciidoc-override.css',
'-C', '/etc/asciidoc/stylesheets/',
'asciidoc.css',
'-C', '/etc/asciidoc/javascripts/',
'asciidoc.js',
'-C', '/etc/asciidoc/',
'images',
],
output : ['website.tar.gz'],
build_by_default : false,
depends : html_targets,
)
meson.add_install_script(MESON_MAKE_SYMLINK,
'repo-add.8',
join_paths(MANDIR, 'man8/repo-remove.8'))
doxygen = find_program('doxygen', required : get_option('doxygen'))
if doxygen.found() and not get_option('doxygen').disabled()
doxyconf = configuration_data()
doxyconf.set('OUTPUT_DIRECTORY', meson.current_build_dir())
doxyfile = configure_file(
input : 'Doxyfile.in',
output : 'Doxyfile',
configuration : doxyconf,
install : false)
custom_target(
'doxygen',
input : doxyfile,
output : ['man3'],
command : [doxygen, doxyfile],
build_by_default : true,
install : true,
install_dir : MANDIR)
endif

33
lib/libalpm/meson.build Normal file
View file

@ -0,0 +1,33 @@
libalpm_sources = files('''
add.h add.c
alpm.h alpm.c
alpm_list.h alpm_list.c
backup.h backup.c
base64.h base64.c
be_local.c
be_package.c
be_sync.c
conflict.h conflict.c
db.h db.c
delta.h delta.c
deps.h deps.c
diskspace.h diskspace.c
dload.h dload.c
error.c
filelist.h filelist.c
graph.h graph.c
group.h group.c
handle.h handle.c
hook.h hook.c
libarchive-compat.h
log.h log.c
package.h package.c
pkghash.h pkghash.c
rawstr.c
remove.h remove.c
signing.c signing.h
sync.h sync.c
trans.h trans.c
util.h util.c
version.c
'''.split())

View file

@ -0,0 +1,15 @@
i18n.gettext(
'libalpm',
args : [
'--directory=@0@'.format(meson.current_source_dir()),
'--msgid-bugs-address=http://bugs.archlinux.org/index.php?project=3',
'--copyright-holder="Pacman Development Team <pacman-dev@archlinux.org>"',
'--language', 'c',
'--keyword=_',
'--flag=_:1:c-format',
'--keyword=_n:1,2',
'--flag=_n:1:c-format',
'--flag=_n:2:c-format',
])

487
meson.build Normal file
View file

@ -0,0 +1,487 @@
project('pacman',
'c',
version : '5.1.0',
license : 'GPLv2+',
default_options : [
'c_std=gnu99',
'prefix=/usr',
'sysconfdir=/etc',
'localstatedir=/var',
],
meson_version : '>= 0.47')
libalpm_version = '11.0.1'
cc = meson.get_compiler('c')
# commandline options
PREFIX = get_option('prefix')
DATAROOTDIR = join_paths(PREFIX, get_option('datarootdir'))
SYSCONFDIR = join_paths(PREFIX, get_option('sysconfdir'))
LOCALSTATEDIR = join_paths(PREFIX, get_option('localstatedir'))
LOCALEDIR = join_paths(PREFIX, get_option('localedir'))
ROOTDIR = get_option('root-dir')
BINDIR = join_paths(PREFIX, get_option('bindir'))
MANDIR = join_paths(PREFIX, get_option('mandir'))
BUILDSCRIPT = get_option('buildscript')
LIBMAKEPKGDIR = join_paths(PREFIX, DATAROOTDIR, 'makepkg')
PKGDATADIR = join_paths(PREFIX, DATAROOTDIR, meson.project_name())
PYTHON = import('python').find_installation('python3')
M4 = find_program('m4')
SED = find_program('sed')
DU = find_program('du')
LDCONFIG = get_option('ldconfig')
MESON_MAKE_SYMLINK = join_paths(meson.source_root(), 'build-aux/meson-make-symlink.sh')
BASH = find_program('bash4', 'bash')
if BASH.found()
bash_version = run_command(BASH, '-c', 'IFS=.; echo "${BASH_VERSINFO[*]:0:3}"').stdout()
have_bash = bash_version.version_compare('>= 4.4.0')
endif
if not have_bash
error('bash >= 4.4.0 is required for pacman scripts.')
endif
bashcompletion = dependency('bash-completion', required : false)
if bashcompletion.found()
BASHCOMPDIR = bashcompletion.get_pkgconfig_variable('completionsdir')
else
BASHCOMPDIR = join_paths(DATAROOTDIR, 'bash-completion/completions')
endif
if get_option('use-git-version')
PACKAGE_VERSION = run_command(
find_program('git'),
'describe',
'--abbrev=4',
'--dirty').stdout().strip().strip('v')
else
PACKAGE_VERSION = meson.project_version()
endif
conf = configuration_data()
conf.set('_GNU_SOURCE', true)
conf.set_quoted('PACKAGE', meson.project_name())
conf.set_quoted('PACKAGE_VERSION', PACKAGE_VERSION)
conf.set_quoted('LOCALEDIR', LOCALEDIR)
conf.set_quoted('SCRIPTLET_SHELL', get_option('scriptlet-shell'))
conf.set_quoted('LDCONFIG', LDCONFIG)
conf.set_quoted('LIB_VERSION', meson.project_version())
conf.set_quoted('SYSHOOKDIR', join_paths(DATAROOTDIR, 'libalpm/hooks/'))
conf.set_quoted('CONFFILE', join_paths(SYSCONFDIR, 'pacman.conf'))
conf.set_quoted('DBPATH', join_paths(LOCALSTATEDIR, 'lib/pacman'))
conf.set_quoted('GPGDIR', join_paths(SYSCONFDIR, 'pacman.d/gnupg'))
conf.set_quoted('LOGFILE', join_paths(LOCALSTATEDIR, 'log/pacman.log'))
conf.set_quoted('CACHEDIR', join_paths(LOCALSTATEDIR, 'cache/pacman/pkg'))
conf.set_quoted('HOOKDIR', join_paths(SYSCONFDIR, 'pacman.d/hooks/'))
conf.set_quoted('ROOTDIR', ROOTDIR)
if get_option('i18n')
if not cc.has_function('ngettext')
error('ngettext not found but NLS support requested')
endif
conf.set('ENABLE_NLS', 1)
endif
# dependencies
libarchive = dependency('libarchive',
version : '>=3.0.0',
static : get_option('buildstatic'))
libcurl = dependency('libcurl',
version : '>=7.32.0',
required : get_option('curl'),
static : get_option('buildstatic'))
conf.set('HAVE_LIBCURL', libcurl.found())
want_gpgme = get_option('gpgme')
gpgme_config = find_program('gpgme-config', required : want_gpgme)
if not want_gpgme.disabled() and gpgme_config.found()
gpgme_version = run_command(gpgme_config, '--version').stdout().strip()
needed_gpgme_version = '>=1.3.0'
have = gpgme_version.version_compare(needed_gpgme_version)
if want_gpgme.enabled() and not have
error('gpgme @0@ is needed for GPG signature support'.format(needed_gpgme_version))
endif
gpgme_libs = [
cc.find_library('gpgme', required : have,
dirs : [get_option('gpgme-libdir')]),
cc.find_library('gpg-error', required : have,
dirs : [get_option('gpgme-libdir')]),
cc.find_library('assuan', required : have,
dirs : [get_option('gpgme-libdir')]),
]
conf.set('HAVE_LIBGPGME', have)
else
gpgme_libs = []
conf.set('HAVE_LIBGPGME', false)
endif
want_crypto = get_option('crypto')
if want_crypto == 'openssl'
libcrypto = dependency('libcrypto', static : get_option('buildstatic'))
if not libcrypto.found()
error('openssl support requested but not found')
endif
crypto_provider = libcrypto
conf.set10('HAVE_LIBSSL', true)
elif want_crypto == 'nettle'
libnettle = dependency('nettle', static : get_option('buildstatic'))
if not libnettle.found()
error('nettle support requested but not found')
endif
crypto_provider = libnettle
conf.set10('HAVE_LIBNETTLE', true)
else
error('unhandled crypto value @0@'.format(want_crypto))
endif
foreach header : [
'mntent.h',
'sys/mnttab.h',
'sys/mount.h',
'sys/param.h',
'sys/statvfs.h',
'sys/types.h',
'sys/ucred.h',
'termios.h',
]
if cc.has_header(header)
conf.set('HAVE_' + header.underscorify().to_upper(), true)
endif
endforeach
foreach sym : [
'dup2',
'fork',
'getcwd',
'getmntent',
'getmntinfo',
'gettimeofday',
'memmove',
'memset',
'mkdir',
'realpath',
'regcomp',
'rmdir',
'setenv',
'setlocale',
'strcasecmp',
'strchr',
'strcspn',
'strdup',
'strerror',
'strndup',
'strnlen',
'strnlen',
'strrchr',
'strsep',
'strsep',
'strstr',
'strtol',
'swprintf',
'tcflush',
'tcflush',
'uname',
'wcwidth',
]
have = cc.has_function(sym, args : '-D_GNU_SOURCE')
conf.set10('HAVE_' + sym.to_upper(), have)
endforeach
foreach member : [
['struct stat', 'st_blksize', '''#include <sys/stat.h>'''],
['struct statvfs', 'f_flag', '''#include <sys/statvfs.h>'''],
['struct statfs', 'f_flags', '''#include <sys/param.h>
#include <sys/mount.h>'''],
]
have = cc.has_member(member[0], member[1], prefix : member[2])
conf.set('HAVE_' + '_'.join([member[0], member[1]]).underscorify().to_upper(), have)
endforeach
if conf.has('HAVE_STRUCT_STATVFS_F_FLAG')
conf.set('FSSTATSTYPE', 'struct statvfs')
elif conf.has('HAVE_STRUCT_STATFS_F_FLAGS')
conf.set('FSSTATSTYPE', 'struct statfs')
endif
if get_option('buildtype') == 'debug'
extra_cflags = [
'-Wcast-align',
'-Wclobbered',
'-Wempty-body',
'-Wfloat-equal',
'-Wformat-nonliteral',
'-Wformat-security',
'-Wignored-qualifiers',
'-Winit-self',
'-Wlogical-op',
'-Wmissing-declarations',
'-Wmissing-field-initializers',
'-Wmissing-parameter-type',
'-Wmissing-prototypes',
'-Wold-style-declaration',
'-Woverride-init',
'-Wpointer-arith',
'-Wredundant-decls',
'-Wshadow',
'-Wsign-compare',
'-Wstrict-aliasing',
'-Wstrict-overflow=5',
'-Wstrict-prototypes',
'-Wtype-limits',
'-Wuninitialized',
'-Wunused-but-set-parameter',
'-Wunused-parameter',
'-Wwrite-strings',
]
add_project_arguments(cc.get_supported_arguments(extra_cflags), language : 'c')
conf.set('PACMAN_DEBUG', 1)
endif
config_h = configure_file(
output : 'config.h',
configuration : conf)
add_project_arguments('-include', 'config.h', language : 'c')
default_duflags = ' -sk --apparent-size'
default_sedinplaceflags = ' --follow-symlinks -i'
inodecmd = 'stat -c \'%i %n\''
ownercmd = 'stat -c \'%u:%g\''
modecmd = 'stat -c \'%a\''
strip_binaries = '--strip-all'
strip_shared = '--strip-unneeded'
strip_static = '--strip-debug'
os = host_machine.system()
if os.startswith('darwin')
inodecmd = '/usr/bin/stat -f \'%i %n\''
ownercmd = '/usr/bin/stat -f \'%u:%g\''
modecmd = '/usr/bin/stat -f \'%lp\''
default_sedinplaceflags = ' -i \'\''
default_duflags = ' -sk'
strip_binaries = ''
strip_shared = '-s'
strip_static = '-s'
elif os.contains('bsd') or os == 'dragonfly'
inodecmd = 'stat -f \'%i %n\''
ownercmd = 'stat -f \'%u:%g\''
modecmd = 'stat -f \'%lp\''
default_sedinplaceflags = ' -i \'\''
default_duflags = ' -sk'
endif
duflags = get_option('duflags')
if duflags == 'autodetect'
duflags = default_duflags
endif
sedinplaceflags = get_option('sedinplaceflags')
if sedinplaceflags == 'auto'
sedinplaceflags = default_sedinplaceflags
endif
chost = run_command(cc, '-dumpmachine').stdout().strip()
carch = chost.split('-')[0]
# annoyingly, we have to maintain two sets of configuration_data which is
# largely identical, but which distinguishes between quoting needs.
substs = configuration_data()
substs.set('SED', SED.path())
substs.set('M4', M4.path())
substs.set('CARCH', carch)
substs.set('CHOST', chost)
substs.set('PKGEXT', get_option('pkg-ext'))
substs.set('SRCEXT', get_option('src-ext'))
substs.set('ROOTDIR', ROOTDIR)
substs.set('LOCALEDIR', LOCALEDIR)
substs.set('sysconfdir', SYSCONFDIR)
substs.set('localstatedir', LOCALSTATEDIR)
substs.set('PKGDATADIR', PKGDATADIR)
substs.set('PREFIX', PREFIX)
substs.set('BASH', BASH.path())
substs.set('PACKAGE_VERSION', PACKAGE_VERSION)
substs.set('PACKAGE_NAME', meson.project_name())
substs.set('BUILDSCRIPT', BUILDSCRIPT)
substs.set('TEMPLATE_DIR', get_option('makepkg-template-dir'))
substs.set('DEBUGSUFFIX', get_option('debug-suffix'))
substs.set('INODECMD', inodecmd)
substs.set('OWNERCMD', ownercmd)
substs.set('MODECMD', modecmd)
substs.set('SEDINPLACEFLAGS', sedinplaceflags)
substs.set('SEDPATH', SED.path())
substs.set('DUFLAGS', duflags)
substs.set('DUPATH', DU.path())
substs.set('LIBMAKEPKGDIR', LIBMAKEPKGDIR)
substs.set('STRIP_BINARIES', strip_binaries)
substs.set('STRIP_SHARED', strip_shared)
substs.set('STRIP_STATIC', strip_static)
subdir('lib/libalpm')
subdir('src/common')
subdir('src/pacman')
subdir('src/util')
subdir('scripts')
# Internationalization
if get_option('i18n')
i18n = import('i18n')
subdir('lib/libalpm/po')
subdir('src/pacman/po')
subdir('scripts/po')
endif
want_doc = get_option('doc')
ASCIIDOC = find_program('asciidoc', required : want_doc)
A2X = find_program('a2x', required : want_doc)
build_doc = A2X.found() and not want_doc.disabled()
if build_doc
subdir('doc')
endif
includes = include_directories('src/common', 'lib/libalpm')
libcommon = static_library(
'common',
libcommon_sources,
include_directories : includes,
install : false)
libalpm = library(
'alpm',
libalpm_sources,
version : libalpm_version,
include_directories : includes,
dependencies : [crypto_provider, libarchive, libcurl] + gpgme_libs,
link_with : [libcommon],
install : true)
install_headers(
'lib/libalpm/alpm.h',
'lib/libalpm/alpm_list.h')
# TODO: libs.private seem quite wrong here
pkgconfig = import('pkgconfig')
pkgconfig.generate(
libalpm,
name : 'libalpm',
description : 'Arch Linux package management library',
version : libalpm_version,
url : 'http://www.archlinux.org/pacman/')
pacman_bin = executable(
'pacman',
pacman_sources,
include_directories : includes,
link_with : [libalpm, libcommon],
dependencies : [libarchive],
install : true,
)
executable(
'pacman-conf',
pacman_conf_sources,
include_directories : includes,
link_with : [libalpm],
install : true,
)
executable(
'cleanupdelta',
cleanupdelta_sources,
include_directories : includes,
link_with : [libalpm],
install : true,
)
executable(
'testpkg',
testpkg_sources,
include_directories : includes,
link_with : [libalpm],
install : true,
)
executable(
'vercmp',
vercmp_sources,
include_directories : includes,
link_with : [libalpm],
install : true,
)
configure_file(
input : 'etc/makepkg.conf.in',
output : 'makepkg.conf',
configuration : substs,
install_dir : SYSCONFDIR)
configure_file(
input : 'etc/pacman.conf.in',
output : 'pacman.conf',
configuration : substs,
install_dir : SYSCONFDIR)
install_data(
'proto/PKGBUILD-split.proto',
'proto/PKGBUILD-vcs.proto',
'proto/PKGBUILD.proto',
'proto/proto.install',
install_dir : join_paths(DATAROOTDIR, 'pacman'))
TEST_ENV = environment()
TEST_ENV.set('PMTEST_SCRIPTLIB_DIR', join_paths(meson.source_root(), 'scripts/library/'))
TEST_ENV.set('PMTEST_LIBMAKEPKG_DIR', join_paths(meson.build_root(), 'scripts/libmakepkg/'))
TEST_ENV.set('PMTEST_UTIL_DIR', meson.build_root() + '/')
TEST_ENV.set('PMTEST_SCRIPT_DIR', join_paths(meson.build_root(), 'scripts/'))
subdir('test/pacman')
subdir('test/scripts')
subdir('test/util')
message('\n '.join([
'@0@ @1@'.format(meson.project_name(), meson.project_version()),
'Build information:',
' prefix : @0@'.format(PREFIX),
' sysconfdir : @0@'.format(SYSCONFDIR),
' conf file : @0@'.format(join_paths(SYSCONFDIR, 'pacman.conf')),
' localstatedir : @0@'.format(LOCALSTATEDIR),
' database dir : @0@'.format(join_paths(LOCALSTATEDIR, 'lib/pacman/')),
' cache dir : @0@'.format(join_paths(LOCALSTATEDIR, 'cache/pacman/pkg/')),
' compiler : @0@ @1@'.format(cc.get_id(), cc.version()),
'',
' Architecture : @0@'.format(carch),
' Host Type : @0@'.format(chost),
' File inode command : @0@'.format(inodecmd),
' File owner command : @0@'.format(ownercmd),
' File mode command : @0@'.format(modecmd),
' Directory size command : @0@ @1@'.format(DU.path(), duflags),
' In-place sed command : @0@ @1@'.format(SED.path(), sedinplaceflags),
' libalpm version : @0@'.format(libalpm_version),
' pacman version : @0@'.format(PACKAGE_VERSION),
'',
'Directory and file information:',
' root working directory : @0@'.format(ROOTDIR),
' package extension : @0@'.format(get_option('pkg-ext')),
' source pkg extension : @0@'.format(get_option('src-ext')),
' build script name : @0@'.format(BUILDSCRIPT),
' template directory : @0@'.format(get_option('makepkg-template-dir')),
'',
'Compilation options:',
' i18n support : @0@'.format(get_option('i18n')),
' Build docs : @0@'.format(build_doc),
' debug build : @0@'.format(get_option('buildtype') == 'debug'),
' Use libcurl : @0@'.format(conf.get('HAVE_LIBCURL')),
' Use GPGME : @0@'.format(conf.get('HAVE_LIBGPGME')),
' Use OpenSSL : @0@'.format(conf.has('HAVE_LIBSSL') and
conf.get('HAVE_LIBSSL') == 1),
' Use nettle : @0@'.format(conf.has('HAVE_LIBNETTLE') and
conf.get('HAVE_LIBNETTLE') == 1),
'',
]))

61
meson_options.txt Normal file
View file

@ -0,0 +1,61 @@
# build behavior
option('use-git-version', type : 'boolean', value : false,
description : 'take version information from git')
option('buildstatic', type : 'boolean', value : false,
description : 'if true, build staticly linked binaries')
# directories and filenames
option('root-dir', type : 'string', value : '/',
description : 'set the location of the root operating directory')
option('pkg-ext', type : 'string', value : '.pkg.tar.gz',
description : 'set the file extension used by packages')
option('src-ext', type : 'string', value : '.src.tar.gz',
description : 'set the file extension used by source packages')
option('scriptlet-shell', type : 'string', value : '/bin/sh',
description : 'The full path of the shell used to run install scriptlets')
option('ldconfig', type : 'string', value : '/sbin/ldconfig',
description : 'set the full path to ldconfig')
option('buildscript', type : 'string', value : 'PKGBUILD',
description : 'set the build script name used by makepkg')
option('datarootdir', type : 'string', value : 'share',
description : 'FIXME')
option('makepkg-template-dir', type : 'string', value : '/usr/share/makepkg-template',
description : 'template dir used by makepkg-template')
option('debug-suffix', type : 'string', value : 'debug',
description : 'suffix for split debugging symbol packages used by makepkg')
# dependencies, features
option('doc', type : 'feature', value : 'auto',
description : 'generate docs and manpages')
option('doxygen', type : 'feature', value : 'disabled',
description : 'generate doxygen manpages and html')
option('curl', type : 'feature', value : 'auto',
description : 'use curl to download files')
option('crypto', type : 'combo', choices : ['openssl', 'nettle'],
description : 'select crypto implementation')
option('gpgme', type : 'feature', value : 'auto',
description : 'use GPGME for PGP signature verification')
option('gpgme-libdir', type : 'string', value : '/usr/lib',
description : 'search directory for gpgme libraries.')
option('i18n', type : 'boolean', value : true,
description : 'enable localization of pacman, libalpm and scripts')
# tools
option('duflags', type : 'string', value : 'autodetect',
description : 'flags to pass to du to measure file size')
option('sedinplaceflags', type : 'string', value : 'auto',
description : 'flags to pass to sed to edit a file in-place')

View file

@ -0,0 +1,20 @@
libmakepkg_module = 'integrity'
sources = [
'generate_checksum.sh.in',
'generate_signature.sh.in',
'verify_checksum.sh.in',
'verify_signature.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

View file

@ -0,0 +1,18 @@
libmakepkg_module = 'lint_config'
sources = [
'paths.sh.in',
'variable.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

View file

@ -0,0 +1,20 @@
libmakepkg_module = 'lint_package'
sources = [
'build_references.sh.in',
'dotfiles.sh.in',
'file_names.sh.in',
'missing_backup.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

View file

@ -0,0 +1,37 @@
libmakepkg_module = 'lint_pkgbuild'
sources = [
'arch.sh.in',
'backup.sh.in',
'changelog.sh.in',
'checkdepends.sh.in',
'conflicts.sh.in',
'depends.sh.in',
'epoch.sh.in',
'install.sh.in',
'makedepends.sh.in',
'optdepends.sh.in',
'options.sh.in',
'package_function.sh.in',
'pkgbase.sh.in',
'pkglist.sh.in',
'pkgname.sh.in',
'pkgrel.sh.in',
'pkgver.sh.in',
'provides.sh.in',
'source.sh.in',
'util.sh.in',
'variable.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

View file

@ -0,0 +1,31 @@
libmakepkg_modules = [
{ 'name' : 'integrity', 'has_subdir' : true },
{ 'name' : 'lint_config', 'has_subdir' : true },
{ 'name' : 'lint_package', 'has_subdir' : true },
{ 'name' : 'lint_pkgbuild', 'has_subdir' : true },
{ 'name' : 'source', 'has_subdir' : true },
{ 'name' : 'srcinfo', },
{ 'name' : 'tidy', 'has_subdir' : true },
{ 'name' : 'util', 'has_subdir' : true },
]
mkdir_p = 'mkdir -p $DESTDIR/@0@'
foreach module : libmakepkg_modules
custom_target(
'libmakepkg_@0@'.format(module['name']),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : '@0@.sh.in'.format(module['name']),
output : '@BASENAME@',
install : true,
install_dir : join_paths(get_option('datadir'), 'makepkg'))
if module.get('has_subdir', false)
subdir(module['name'])
path = join_paths(get_option('prefix'),
get_option('datadir'),
'makepkg',
module['name'])
meson.add_install_script('sh', '-c', mkdir_p.format(path))
endif
endforeach

View file

@ -0,0 +1,22 @@
libmakepkg_module = 'source'
sources = [
'bzr.sh.in',
'file.sh.in',
'git.sh.in',
'hg.sh.in',
'local.sh.in',
'svn.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

View file

@ -0,0 +1,23 @@
libmakepkg_module = 'tidy'
sources = [
'docs.sh.in',
'emptydirs.sh.in',
'libtool.sh.in',
'purge.sh.in',
'staticlibs.sh.in',
'strip.sh.in',
'zipman.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

View file

@ -0,0 +1,24 @@
libmakepkg_module = 'util'
sources = [
'compress.sh.in',
'error.sh.in',
'message.sh.in',
'option.sh.in',
'parseopts.sh.in',
'pkgbuild.sh.in',
'source.sh.in',
'util.sh.in',
]
foreach src : sources
output_dir = join_paths(get_option('datadir'), 'makepkg', libmakepkg_module)
custom_target(
libmakepkg_module + '_' + src.underscorify(),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : src,
output : '@BASENAME@',
install : true,
install_dir : output_dir)
endforeach

66
scripts/meson.build Normal file
View file

@ -0,0 +1,66 @@
scripts = [
'makepkg-template.pl.in',
'makepkg.sh.in',
'pacman-db-upgrade.sh.in',
'pacman-key.sh.in',
'pkgdelta.sh.in',
'repo-add.sh.in'
]
library_files = [
'library/human_to_size.sh',
'library/size_to_human.sh',
]
SCRIPT_EDITOR = find_program(configure_file(
input : join_paths(meson.source_root(), 'build-aux/edit-script.sh.in'),
output : 'edit-script.sh',
configuration : substs))
m4_edit = generator(
M4,
arguments : ['-P', '-I', meson.current_source_dir(), '@INPUT@'],
output : '@PLAINNAME@',
capture : true)
foreach script : scripts
custom_target(
script,
input : m4_edit.process(script),
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@', '0755'],
output : script.split('.')[0],
depend_files : library_files,
install : true,
install_dir : get_option('bindir'))
endforeach
foreach symlink : ['repo-remove', 'repo-elephant']
meson.add_install_script(MESON_MAKE_SYMLINK,
'repo-add',
join_paths(BINDIR, symlink))
endforeach
subdir('libmakepkg')
custom_target(
'bash_completion',
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : 'completion/bash_completion.in',
output : 'pacman',
install : true,
install_dir : BASHCOMPDIR)
foreach symlink : ['pacman-key', 'makepkg']
meson.add_install_script(MESON_MAKE_SYMLINK,
'pacman',
join_paths(BASHCOMPDIR, symlink))
endforeach
zsh_completion_dir = join_paths(DATAROOTDIR, 'zsh/site-functions')
custom_target(
'zsh_completion',
command : [ SCRIPT_EDITOR, '@INPUT@', '@OUTPUT@' ],
input : 'completion/zsh_completion.in',
output : '_pacman',
install : true,
install_dir : zsh_completion_dir)

15
scripts/po/meson.build Normal file
View file

@ -0,0 +1,15 @@
i18n.gettext(
'pacman-scripts',
args : [
'--directory=@0@'.format(meson.current_source_dir()),
'--msgid-bugs-address=http://bugs.archlinux.org/index.php?project=3',
'--copyright-holder="Pacman Development Team <pacman-dev@archlinux.org>"',
'--language', 'shell',
'--keyword=_',
'--flag=_:1:c-format',
'--keyword=_n:1,2',
'--flag=_n:1:c-format',
'--flag=_n:2:c-format',
])

4
src/common/meson.build Normal file
View file

@ -0,0 +1,4 @@
libcommon_sources = files('''
ini.c ini.h
util-common.c util-common.h
'''.split())

23
src/pacman/meson.build Normal file
View file

@ -0,0 +1,23 @@
pacman_sources = files('''
check.h check.c
conf.h conf.c
database.c
deptest.c
files.c
package.h package.c
pacman.h pacman.c
query.c
remove.c
sighandler.h sighandler.c
sync.c
callback.h callback.c
upgrade.c
util.h util.c
'''.split())
pacman_conf_sources = files('''
pacman-conf.c
util.h util.c
callback.h callback.c
conf.h conf.c
'''.split())

15
src/pacman/po/meson.build Normal file
View file

@ -0,0 +1,15 @@
i18n.gettext(
'pacman',
args : [
'--directory=@0@'.format(meson.current_source_dir()),
'--msgid-bugs-address=http://bugs.archlinux.org/index.php?project=3',
'--copyright-holder="Pacman Development Team <pacman-dev@archlinux.org>"',
'--language', 'c',
'--keyword=_',
'--flag=_:1:c-format',
'--keyword=_n:1,2',
'--flag=_n:1:c-format',
'--flag=_n:2:c-format',
])

3
src/util/meson.build Normal file
View file

@ -0,0 +1,3 @@
cleanupdelta_sources = files('cleanupdelta.c')
testpkg_sources = files('testpkg.c')
vercmp_sources = files('vercmp.c')

357
test/pacman/meson.build Normal file
View file

@ -0,0 +1,357 @@
pacman_tests = [
{ 'name': 'tests/backup001.py' },
{ 'name': 'tests/clean001.py' },
{ 'name': 'tests/clean002.py' },
{ 'name': 'tests/clean003.py' },
{ 'name': 'tests/clean004.py' },
{ 'name': 'tests/clean005.py' },
{ 'name': 'tests/config001.py' },
{ 'name': 'tests/config002.py' },
{ 'name': 'tests/database001.py' },
{ 'name': 'tests/database002.py' },
{ 'name': 'tests/database010.py' },
{ 'name': 'tests/database011.py' },
{ 'name': 'tests/database012.py' },
{ 'name': 'tests/dbonly-extracted-files.py' },
{ 'name': 'tests/depconflict100.py' },
{ 'name': 'tests/depconflict110.py' },
{ 'name': 'tests/depconflict111.py' },
{ 'name': 'tests/depconflict120.py' },
{ 'name': 'tests/dependency-cycle-fixed-by-upgrade.py' },
{ 'name': 'tests/deprange001.py',
'should_fail': true },
{ 'name': 'tests/deptest001.py' },
{ 'name': 'tests/dummy001.py' },
{ 'name': 'tests/epoch001.py' },
{ 'name': 'tests/epoch002.py' },
{ 'name': 'tests/epoch003.py' },
{ 'name': 'tests/epoch004.py' },
{ 'name': 'tests/epoch005.py' },
{ 'name': 'tests/epoch010.py' },
{ 'name': 'tests/epoch011.py' },
{ 'name': 'tests/epoch012.py' },
{ 'name': 'tests/file-conflict-with-installed-pkg.py' },
{ 'name': 'tests/fileconflict001.py' },
{ 'name': 'tests/fileconflict002.py' },
{ 'name': 'tests/fileconflict003.py' },
{ 'name': 'tests/fileconflict004.py' },
{ 'name': 'tests/fileconflict005.py' },
{ 'name': 'tests/fileconflict006.py' },
{ 'name': 'tests/fileconflict007.py' },
{ 'name': 'tests/fileconflict008.py' },
{ 'name': 'tests/fileconflict009.py' },
{ 'name': 'tests/fileconflict010.py' },
{ 'name': 'tests/fileconflict011.py' },
{ 'name': 'tests/fileconflict012.py' },
{ 'name': 'tests/fileconflict013.py' },
{ 'name': 'tests/fileconflict015.py' },
{ 'name': 'tests/fileconflict016.py' },
{ 'name': 'tests/fileconflict017.py' },
{ 'name': 'tests/fileconflict020.py' },
{ 'name': 'tests/fileconflict021.py' },
{ 'name': 'tests/fileconflict022.py' },
{ 'name': 'tests/fileconflict023.py' },
{ 'name': 'tests/fileconflict024.py' },
{ 'name': 'tests/fileconflict025.py' },
{ 'name': 'tests/fileconflict030.py' },
{ 'name': 'tests/fileconflict031.py' },
{ 'name': 'tests/fileconflict032.py' },
{ 'name': 'tests/hook-abortonfail.py' },
{ 'name': 'tests/hook-description-reused.py' },
{ 'name': 'tests/hook-exec-reused.py' },
{ 'name': 'tests/hook-exec-with-arguments.py' },
{ 'name': 'tests/hook-file-change-packages.py' },
{ 'name': 'tests/hook-file-remove-trigger-match.py' },
{ 'name': 'tests/hook-file-upgrade-nomatch.py' },
{ 'name': 'tests/hook-invalid-trigger.py' },
{ 'name': 'tests/hook-pkg-install-trigger-match.py' },
{ 'name': 'tests/hook-pkg-postinstall-trigger-match.py' },
{ 'name': 'tests/hook-pkg-remove-trigger-match.py' },
{ 'name': 'tests/hook-pkg-upgrade-trigger-match.py' },
{ 'name': 'tests/hook-target-list.py' },
{ 'name': 'tests/hook-type-reused.py' },
{ 'name': 'tests/hook-upgrade-trigger-no-match.py' },
{ 'name': 'tests/hook-when-reused.py' },
{ 'name': 'tests/ignore001.py' },
{ 'name': 'tests/ignore002.py' },
{ 'name': 'tests/ignore003.py' },
{ 'name': 'tests/ignore004.py' },
{ 'name': 'tests/ignore005.py' },
{ 'name': 'tests/ignore006.py' },
{ 'name': 'tests/ignore007.py' },
{ 'name': 'tests/ignore008.py' },
{ 'name': 'tests/ldconfig001.py' },
{ 'name': 'tests/ldconfig002.py' },
{ 'name': 'tests/ldconfig003.py' },
{ 'name': 'tests/mode001.py' },
{ 'name': 'tests/mode002.py' },
{ 'name': 'tests/mode003.py' },
{ 'name': 'tests/noupgrade-inverted.py' },
{ 'name': 'tests/overwrite-files-match-negated.py' },
{ 'name': 'tests/overwrite-files-match.py' },
{ 'name': 'tests/overwrite-files-nonmatch.py' },
{ 'name': 'tests/pacman001.py' },
{ 'name': 'tests/pacman002.py' },
{ 'name': 'tests/pacman003.py' },
{ 'name': 'tests/pacman004.py' },
{ 'name': 'tests/pacman005.py' },
{ 'name': 'tests/provision001.py' },
{ 'name': 'tests/provision002.py' },
{ 'name': 'tests/provision003.py' },
{ 'name': 'tests/provision004.py' },
{ 'name': 'tests/provision010.py' },
{ 'name': 'tests/provision011.py' },
{ 'name': 'tests/provision012.py' },
{ 'name': 'tests/provision020.py' },
{ 'name': 'tests/provision021.py' },
{ 'name': 'tests/provision022.py' },
{ 'name': 'tests/query001.py' },
{ 'name': 'tests/query002.py' },
{ 'name': 'tests/query003.py' },
{ 'name': 'tests/query004.py' },
{ 'name': 'tests/query005.py' },
{ 'name': 'tests/query006.py',
# expect failure on 32 bit machines
'should_fail': cc.sizeof('ssize_t') < 8 },
{ 'name': 'tests/query007.py' },
{ 'name': 'tests/query010.py' },
{ 'name': 'tests/query011.py' },
{ 'name': 'tests/query012.py' },
{ 'name': 'tests/querycheck001.py' },
{ 'name': 'tests/querycheck002.py' },
{ 'name': 'tests/querycheck_fast_file_type.py' },
{ 'name': 'tests/reason001.py' },
{ 'name': 'tests/remove-assumeinstalled.py' },
{ 'name': 'tests/remove-directory-replaced-with-symlink.py' },
{ 'name': 'tests/remove-optdepend-of-installed-package.py' },
{ 'name': 'tests/remove-recursive-cycle.py' },
{ 'name': 'tests/remove001.py' },
{ 'name': 'tests/remove002.py' },
{ 'name': 'tests/remove010.py' },
{ 'name': 'tests/remove011.py' },
{ 'name': 'tests/remove012.py' },
{ 'name': 'tests/remove020.py' },
{ 'name': 'tests/remove021.py' },
{ 'name': 'tests/remove030.py' },
{ 'name': 'tests/remove031.py' },
{ 'name': 'tests/remove040.py' },
{ 'name': 'tests/remove041.py' },
{ 'name': 'tests/remove042.py' },
{ 'name': 'tests/remove043.py' },
{ 'name': 'tests/remove044.py' },
{ 'name': 'tests/remove045.py' },
{ 'name': 'tests/remove047.py' },
{ 'name': 'tests/remove049.py' },
{ 'name': 'tests/remove050.py' },
{ 'name': 'tests/remove051.py' },
{ 'name': 'tests/remove052.py' },
{ 'name': 'tests/remove060.py' },
{ 'name': 'tests/remove070.py' },
{ 'name': 'tests/remove071.py' },
{ 'name': 'tests/replace100.py' },
{ 'name': 'tests/replace101.py' },
{ 'name': 'tests/replace102.py' },
{ 'name': 'tests/replace103.py' },
{ 'name': 'tests/replace104.py' },
{ 'name': 'tests/replace110.py',
'should_fail': true },
{ 'name': 'tests/scriptlet001.py' },
{ 'name': 'tests/scriptlet002.py' },
{ 'name': 'tests/sign001.py' },
{ 'name': 'tests/sign002.py' },
{ 'name': 'tests/skip-remove-with-glob-chars.py' },
{ 'name': 'tests/smoke001.py' },
{ 'name': 'tests/smoke002.py' },
{ 'name': 'tests/smoke003.py' },
{ 'name': 'tests/smoke004.py' },
{ 'name': 'tests/symlink-replace-with-dir.py' },
{ 'name': 'tests/symlink001.py' },
{ 'name': 'tests/symlink002.py' },
{ 'name': 'tests/symlink010.py' },
{ 'name': 'tests/symlink011.py' },
{ 'name': 'tests/symlink012.py' },
{ 'name': 'tests/symlink020.py' },
{ 'name': 'tests/symlink021.py' },
{ 'name': 'tests/sync-install-assumeinstalled.py' },
{ 'name': 'tests/sync-nodepversion01.py' },
{ 'name': 'tests/sync-nodepversion02.py' },
{ 'name': 'tests/sync-nodepversion03.py' },
{ 'name': 'tests/sync-nodepversion04.py' },
{ 'name': 'tests/sync-nodepversion05.py' },
{ 'name': 'tests/sync-nodepversion06.py' },
{ 'name': 'tests/sync-sysupgrade-print-replaced-packages.py' },
{ 'name': 'tests/sync-update-assumeinstalled.py' },
{ 'name': 'tests/sync-update-package-removing-required-provides.py',
'should_fail': true },
{ 'name': 'tests/sync001.py' },
{ 'name': 'tests/sync002.py' },
{ 'name': 'tests/sync003.py' },
{ 'name': 'tests/sync009.py' },
{ 'name': 'tests/sync010.py' },
{ 'name': 'tests/sync011.py' },
{ 'name': 'tests/sync012.py' },
{ 'name': 'tests/sync020.py' },
{ 'name': 'tests/sync021.py' },
{ 'name': 'tests/sync022.py' },
{ 'name': 'tests/sync023.py' },
{ 'name': 'tests/sync024.py' },
{ 'name': 'tests/sync030.py' },
{ 'name': 'tests/sync031.py' },
{ 'name': 'tests/sync040.py' },
{ 'name': 'tests/sync041.py' },
{ 'name': 'tests/sync042.py' },
{ 'name': 'tests/sync043.py' },
{ 'name': 'tests/sync044.py' },
{ 'name': 'tests/sync045.py' },
{ 'name': 'tests/sync046.py' },
{ 'name': 'tests/sync050.py' },
{ 'name': 'tests/sync051.py' },
{ 'name': 'tests/sync052.py' },
{ 'name': 'tests/sync100.py' },
{ 'name': 'tests/sync1000.py' },
{ 'name': 'tests/sync1003.py' },
{ 'name': 'tests/sync1004.py' },
{ 'name': 'tests/sync1008.py' },
{ 'name': 'tests/sync101.py' },
{ 'name': 'tests/sync102.py' },
{ 'name': 'tests/sync103.py' },
{ 'name': 'tests/sync104.py' },
{ 'name': 'tests/sync110.py' },
{ 'name': 'tests/sync1100.py' },
{ 'name': 'tests/sync1101.py' },
{ 'name': 'tests/sync1102.py' },
{ 'name': 'tests/sync1103.py' },
{ 'name': 'tests/sync1104.py' },
{ 'name': 'tests/sync1105.py' },
{ 'name': 'tests/sync120.py' },
{ 'name': 'tests/sync130.py' },
{ 'name': 'tests/sync131.py' },
{ 'name': 'tests/sync132.py' },
{ 'name': 'tests/sync133.py' },
{ 'name': 'tests/sync134.py' },
{ 'name': 'tests/sync135.py' },
{ 'name': 'tests/sync136.py' },
{ 'name': 'tests/sync137.py' },
{ 'name': 'tests/sync138.py' },
{ 'name': 'tests/sync139.py' },
{ 'name': 'tests/sync140.py' },
{ 'name': 'tests/sync141.py' },
{ 'name': 'tests/sync150.py' },
{ 'name': 'tests/sync200.py' },
{ 'name': 'tests/sync300.py' },
{ 'name': 'tests/sync306.py' },
{ 'name': 'tests/sync400.py' },
{ 'name': 'tests/sync401.py' },
{ 'name': 'tests/sync402.py' },
{ 'name': 'tests/sync403.py',
'should_fail': true },
{ 'name': 'tests/sync404.py' },
{ 'name': 'tests/sync405.py' },
{ 'name': 'tests/sync406.py',
'should_fail': true },
{ 'name': 'tests/sync407.py' },
{ 'name': 'tests/sync500.py' },
{ 'name': 'tests/sync501.py' },
{ 'name': 'tests/sync502.py' },
{ 'name': 'tests/sync503.py' },
{ 'name': 'tests/sync600.py' },
{ 'name': 'tests/sync700.py' },
{ 'name': 'tests/sync701.py' },
{ 'name': 'tests/sync702.py' },
{ 'name': 'tests/sync890.py' },
{ 'name': 'tests/sync891.py' },
{ 'name': 'tests/sync892.py' },
{ 'name': 'tests/sync893.py' },
{ 'name': 'tests/sync895.py' },
{ 'name': 'tests/sync896.py' },
{ 'name': 'tests/sync897.py' },
{ 'name': 'tests/sync898.py' },
{ 'name': 'tests/sync899.py' },
{ 'name': 'tests/sync900.py' },
{ 'name': 'tests/sync901.py' },
{ 'name': 'tests/sync990.py' },
{ 'name': 'tests/sync992.py' },
{ 'name': 'tests/sync993.py' },
{ 'name': 'tests/sync999.py' },
{ 'name': 'tests/trans001.py' },
{ 'name': 'tests/type001.py' },
{ 'name': 'tests/unresolvable001.py' },
{ 'name': 'tests/upgrade001.py' },
{ 'name': 'tests/upgrade002.py' },
{ 'name': 'tests/upgrade003.py' },
{ 'name': 'tests/upgrade004.py' },
{ 'name': 'tests/upgrade005.py' },
{ 'name': 'tests/upgrade006.py' },
{ 'name': 'tests/upgrade010.py' },
{ 'name': 'tests/upgrade011.py' },
{ 'name': 'tests/upgrade013.py' },
{ 'name': 'tests/upgrade020.py' },
{ 'name': 'tests/upgrade021.py' },
{ 'name': 'tests/upgrade022.py' },
{ 'name': 'tests/upgrade023.py' },
{ 'name': 'tests/upgrade024.py' },
{ 'name': 'tests/upgrade025.py' },
{ 'name': 'tests/upgrade026.py' },
{ 'name': 'tests/upgrade027.py' },
{ 'name': 'tests/upgrade028.py' },
{ 'name': 'tests/upgrade029.py' },
{ 'name': 'tests/upgrade030.py' },
{ 'name': 'tests/upgrade031.py' },
{ 'name': 'tests/upgrade032.py' },
{ 'name': 'tests/upgrade040.py' },
{ 'name': 'tests/upgrade041.py' },
{ 'name': 'tests/upgrade042.py' },
{ 'name': 'tests/upgrade043.py' },
{ 'name': 'tests/upgrade045.py' },
{ 'name': 'tests/upgrade050.py' },
{ 'name': 'tests/upgrade051.py' },
{ 'name': 'tests/upgrade052.py' },
{ 'name': 'tests/upgrade053.py' },
{ 'name': 'tests/upgrade054.py' },
{ 'name': 'tests/upgrade055.py' },
{ 'name': 'tests/upgrade056.py' },
{ 'name': 'tests/upgrade057.py' },
{ 'name': 'tests/upgrade058.py' },
{ 'name': 'tests/upgrade059.py' },
{ 'name': 'tests/upgrade060.py' },
{ 'name': 'tests/upgrade061.py' },
{ 'name': 'tests/upgrade070.py' },
{ 'name': 'tests/upgrade071.py' },
{ 'name': 'tests/upgrade072.py' },
{ 'name': 'tests/upgrade073.py' },
{ 'name': 'tests/upgrade074.py' },
{ 'name': 'tests/upgrade075.py' },
{ 'name': 'tests/upgrade076.py' },
{ 'name': 'tests/upgrade077.py' },
{ 'name': 'tests/upgrade078.py',
'should_fail': true },
{ 'name': 'tests/upgrade080.py' },
{ 'name': 'tests/upgrade081.py' },
{ 'name': 'tests/upgrade082.py' },
{ 'name': 'tests/upgrade083.py' },
{ 'name': 'tests/upgrade084.py' },
{ 'name': 'tests/upgrade090.py' },
{ 'name': 'tests/upgrade100.py' },
{ 'name': 'tests/xfercommand001.py' },
]
foreach testobj : pacman_tests
input = testobj.get('name')
test_name = input.split('/')[1]
should_fail = testobj.get('should_fail', false)
test(
test_name,
PYTHON,
args : [
join_paths(meson.source_root(), 'build-aux/tap-driver.py'),
join_paths(meson.current_source_dir(), 'pactest.py'),
'--scriptlet-shell', get_option('scriptlet-shell'),
'--bindir', meson.build_root(),
'--ldconfig', LDCONFIG,
'--verbose',
join_paths(meson.current_source_dir(), input)
],
depends : [pacman_bin],
should_fail : should_fail)
endforeach

15
test/scripts/meson.build Normal file
View file

@ -0,0 +1,15 @@
tests = [
'parseopts_test.sh',
'makepkg-template_test.sh',
'human_to_size_test.sh',
]
foreach tst : tests
test(tst,
PYTHON,
env : TEST_ENV,
args : [
join_paths(meson.source_root(), 'build-aux/tap-driver.py'),
join_paths(meson.current_source_dir(), tst),
])
endforeach

6
test/util/meson.build Normal file
View file

@ -0,0 +1,6 @@
test('vercmptest',
BASH,
env : TEST_ENV,
args : [
join_paths(meson.current_source_dir(), 'vercmptest.sh')
])