2017-01-06 00:17:47 +08:00
# Copyright 2012-2017 The Meson development team
2013-01-12 04:59:49 +08:00
# 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.
2020-12-08 06:58:23 +08:00
from collections import OrderedDict
2020-05-01 04:39:36 +08:00
from functools import lru_cache
import copy
2018-09-26 01:07:09 +08:00
import hashlib
2020-05-01 04:39:36 +08:00
import itertools , pathlib
import os
2018-03-01 06:25:12 +08:00
import pickle
2020-05-01 04:39:36 +08:00
import re
2020-01-06 22:27:38 +08:00
import typing as T
2017-03-27 17:10:34 +08:00
2016-01-16 03:12:23 +08:00
from . import environment
from . import dependencies
from . import mlog
2018-11-28 13:36:47 +08:00
from . mesonlib import (
2018-10-05 08:52:08 +08:00
File , MesonException , MachineChoice , PerMachine , OrderedSet , listify ,
extract_as_list , typeslistify , stringlistify , classify_unity_sources ,
2020-12-04 03:37:52 +08:00
get_filenames_templates_dict , substitute_values , has_path_sep , unholder ,
OptionKey ,
2018-11-28 13:36:47 +08:00
)
2019-06-13 06:08:45 +08:00
from . compilers import (
2020-12-08 06:58:23 +08:00
Compiler , is_object , clink_langs , sort_clink , lang_suffixes ,
2019-06-13 06:08:45 +08:00
is_known_suffix
)
2019-02-25 15:45:20 +08:00
from . linkers import StaticLinker
2018-06-04 20:32:02 +08:00
from . interpreterbase import FeatureNew
2014-09-14 18:47:18 +08:00
2020-09-01 20:28:08 +08:00
if T . TYPE_CHECKING :
2020-12-03 08:02:03 +08:00
from . coredata import KeyedOptionDictType , OptionDictType
2020-09-01 20:28:08 +08:00
from . interpreter import Test
2020-10-22 07:00:17 +08:00
from . mesonlib import FileMode , FileOrString
2020-09-01 20:28:08 +08:00
2018-03-30 01:36:46 +08:00
pch_kwargs = set ( [ ' c_pch ' , ' cpp_pch ' ] )
lang_arg_kwargs = set ( [
' c_args ' ,
' cpp_args ' ,
2019-01-29 12:14:19 +08:00
' cuda_args ' ,
2018-03-30 01:36:46 +08:00
' d_args ' ,
' d_import_dirs ' ,
' d_unittest ' ,
' d_module_versions ' ,
2018-08-18 20:11:35 +08:00
' d_debug ' ,
2018-03-30 01:36:46 +08:00
' fortran_args ' ,
' java_args ' ,
' objc_args ' ,
' objcpp_args ' ,
' rust_args ' ,
' vala_args ' ,
' cs_args ' ,
] )
vala_kwargs = set ( [ ' vala_header ' , ' vala_gir ' , ' vala_vapi ' ] )
rust_kwargs = set ( [ ' rust_crate_type ' ] )
cs_kwargs = set ( [ ' resources ' , ' cs_args ' ] )
buildtarget_kwargs = set ( [
' build_by_default ' ,
' build_rpath ' ,
' dependencies ' ,
' extra_files ' ,
' gui_app ' ,
' link_with ' ,
' link_whole ' ,
' link_args ' ,
' link_depends ' ,
' implicit_include_directories ' ,
' include_directories ' ,
' install ' ,
' install_rpath ' ,
' install_dir ' ,
2018-04-25 12:39:59 +08:00
' install_mode ' ,
2018-03-30 01:36:46 +08:00
' name_prefix ' ,
' name_suffix ' ,
' native ' ,
' objects ' ,
' override_options ' ,
' sources ' ,
2018-08-10 00:46:45 +08:00
' gnu_symbol_visibility ' ,
2020-03-25 03:09:54 +08:00
' link_language ' ,
2020-09-28 01:21:59 +08:00
' win_subsystem ' ,
2018-03-30 01:36:46 +08:00
] )
known_build_target_kwargs = (
buildtarget_kwargs |
lang_arg_kwargs |
pch_kwargs |
vala_kwargs |
rust_kwargs |
cs_kwargs )
2020-03-25 03:09:54 +08:00
known_exe_kwargs = known_build_target_kwargs | { ' implib ' , ' export_dynamic ' , ' pie ' }
2018-08-28 17:47:00 +08:00
known_shlib_kwargs = known_build_target_kwargs | { ' version ' , ' soversion ' , ' vs_module_defs ' , ' darwin_versions ' }
2019-07-15 14:29:49 +08:00
known_shmod_kwargs = known_build_target_kwargs | { ' vs_module_defs ' }
2020-11-14 22:43:00 +08:00
known_stlib_kwargs = known_build_target_kwargs | { ' pic ' , ' prelink ' }
2018-03-30 01:36:46 +08:00
known_jar_kwargs = known_exe_kwargs | { ' main_class ' }
2016-07-01 17:13:51 +08:00
2018-09-30 03:15:00 +08:00
@lru_cache ( maxsize = None )
2019-08-10 04:46:35 +08:00
def get_target_macos_dylib_install_name ( ld ) - > str :
name = [ ' @rpath/ ' , ld . prefix , ld . name ]
if ld . soversion is not None :
name . append ( ' . ' + ld . soversion )
name . append ( ' .dylib ' )
return ' ' . join ( name )
2018-09-30 03:15:00 +08:00
2016-03-23 01:31:55 +08:00
class InvalidArguments ( MesonException ) :
2013-09-24 03:34:41 +08:00
pass
2019-11-26 09:16:54 +08:00
class DependencyOverride :
def __init__ ( self , dep , node , explicit = True ) :
self . dep = dep
self . node = node
self . explicit = explicit
2020-10-17 05:02:53 +08:00
class Headers :
def __init__ ( self , sources : T . List [ File ] , install_subdir : T . Optional [ str ] ,
install_dir : T . Optional [ str ] , install_mode : T . Optional [ ' FileMode ' ] ) :
self . sources = sources
self . install_subdir = install_subdir
self . custom_install_dir = install_dir
self . custom_install_mode = install_mode
# TODO: we really don't need any of these methods, but they're preserved to
# keep APIs relying on them working.
def set_install_subdir ( self , subdir : str ) - > None :
self . install_subdir = subdir
def get_install_subdir ( self ) - > str :
return self . install_subdir
def get_sources ( self ) - > T . List [ File ] :
return self . sources
def get_custom_install_dir ( self ) - > T . Optional [ str ] :
return self . custom_install_dir
def get_custom_install_mode ( self ) - > T . Optional [ ' FileMode ' ] :
return self . custom_install_mode
2020-10-17 05:14:05 +08:00
class Man :
def __init__ ( self , sources : T . List [ File ] , install_dir : T . Optional [ str ] ,
install_mode : T . Optional [ ' FileMode ' ] ) :
self . sources = sources
self . custom_install_dir = install_dir
self . custom_install_mode = install_mode
def get_custom_install_dir ( self ) - > T . Optional [ str ] :
return self . custom_install_dir
def get_custom_install_mode ( self ) - > T . Optional [ ' FileMode ' ] :
return self . custom_install_mode
def get_sources ( self ) - > T . List [ ' File ' ] :
return self . sources
2021-01-13 03:31:25 +08:00
class InstallDir :
def __init__ ( self , src_subdir : str , inst_subdir : str , install_dir : str ,
install_mode : T . Optional [ ' FileMode ' ] ,
exclude : T . Tuple [ T . Set [ str ] , T . Set [ str ] ] ,
strip_directory : bool , from_source_dir : bool = True ) :
self . source_subdir = src_subdir
self . installable_subdir = inst_subdir
self . install_dir = install_dir
self . install_mode = install_mode
self . exclude = exclude
self . strip_directory = strip_directory
self . from_source_dir = from_source_dir
2013-01-12 04:59:49 +08:00
class Build :
""" A class that holds the status of one build including
all dependencies and so on .
"""
2019-05-15 07:39:15 +08:00
def __init__ ( self , environment : environment . Environment ) :
2014-03-18 04:09:28 +08:00
self . project_name = ' name of master project '
2015-08-22 01:50:40 +08:00
self . project_version = None
2013-01-12 04:59:49 +08:00
self . environment = environment
2013-12-09 08:43:28 +08:00
self . projects = { }
2020-12-17 04:26:11 +08:00
self . targets : T . MutableMapping [ str , ' Target ' ] = OrderedDict ( )
self . run_target_names : T . Set [ T . Tuple [ str , str ] ] = set ( )
self . global_args : PerMachine [ T . Dict [ str , T . List [ str ] ] ] = PerMachine ( { } , { } )
self . projects_args : PerMachine [ T . Dict [ str , T . List [ str ] ] ] = PerMachine ( { } , { } )
self . global_link_args : PerMachine [ T . Dict [ str , T . List [ str ] ] ] = PerMachine ( { } , { } )
self . projects_link_args : PerMachine [ T . Dict [ str , T . List [ str ] ] ] = PerMachine ( { } , { } )
self . tests : T . List [ ' Test ' ] = [ ]
self . benchmarks : T . List [ ' Test ' ] = [ ]
2020-10-17 05:02:53 +08:00
self . headers : T . List [ Headers ] = [ ]
2020-10-17 05:14:05 +08:00
self . man : T . List [ Man ] = [ ]
2020-12-17 04:22:20 +08:00
self . data : T . List [ Data ] = [ ]
2020-12-17 04:26:11 +08:00
self . static_linker : PerMachine [ StaticLinker ] = PerMachine ( None , None )
2013-12-10 02:39:53 +08:00
self . subprojects = { }
2018-03-08 23:01:06 +08:00
self . subproject_dir = ' '
2015-07-30 07:01:47 +08:00
self . install_scripts = [ ]
2016-03-01 21:07:38 +08:00
self . postconf_scripts = [ ]
2018-07-18 22:11:57 +08:00
self . dist_scripts = [ ]
2021-01-13 03:31:25 +08:00
self . install_dirs : T . List [ InstallDir ] = [ ]
2015-08-22 01:50:40 +08:00
self . dep_manifest_name = None
self . dep_manifest = { }
2018-10-05 08:52:08 +08:00
self . stdlibs = PerMachine ( { } , { } )
2020-12-17 04:26:11 +08:00
self . test_setups : T . Dict [ str , TestSetup ] = { }
2018-11-12 17:33:49 +08:00
self . test_setup_default_name = None
2018-03-11 20:58:23 +08:00
self . find_overrides = { }
self . searched_programs = set ( ) # The list of all programs that have been searched for.
2018-12-20 09:56:07 +08:00
self . dependency_overrides = PerMachine ( { } , { } )
2013-01-12 04:59:49 +08:00
2018-10-28 23:51:13 +08:00
def copy ( self ) :
other = Build ( self . environment )
for k , v in self . __dict__ . items ( ) :
if isinstance ( v , ( list , dict , set , OrderedDict ) ) :
other . __dict__ [ k ] = v . copy ( )
else :
other . __dict__ [ k ] = v
return other
def merge ( self , other ) :
for k , v in other . __dict__ . items ( ) :
self . __dict__ [ k ] = v
2019-01-10 04:56:52 +08:00
def ensure_static_linker ( self , compiler ) :
2018-10-05 08:52:08 +08:00
if self . static_linker [ compiler . for_machine ] is None and compiler . needs_static_linker ( ) :
self . static_linker [ compiler . for_machine ] = self . environment . detect_static_linker ( compiler )
2013-08-31 03:20:10 +08:00
2013-01-12 04:59:49 +08:00
def get_project ( self ) :
2013-12-09 08:59:15 +08:00
return self . projects [ ' ' ]
2013-01-12 04:59:49 +08:00
2018-03-08 23:01:06 +08:00
def get_subproject_dir ( self ) :
return self . subproject_dir
2020-09-01 20:28:08 +08:00
def get_targets ( self ) - > T . Dict [ str , ' Target ' ] :
2013-01-12 04:59:49 +08:00
return self . targets
2020-09-01 20:28:08 +08:00
def get_tests ( self ) - > T . List [ ' Test ' ] :
2013-01-12 04:59:49 +08:00
return self . tests
2013-01-12 19:53:19 +08:00
2020-09-01 20:28:08 +08:00
def get_benchmarks ( self ) - > T . List [ ' Test ' ] :
2015-11-26 05:29:06 +08:00
return self . benchmarks
2013-01-12 19:53:19 +08:00
def get_headers ( self ) :
return self . headers
2013-01-12 20:31:43 +08:00
def get_man ( self ) :
return self . man
2013-01-14 01:25:54 +08:00
def get_data ( self ) :
return self . data
2013-01-14 07:13:55 +08:00
2014-11-04 05:28:47 +08:00
def get_install_subdirs ( self ) :
return self . install_dirs
2018-10-05 08:52:08 +08:00
def get_global_args ( self , compiler , for_machine ) :
d = self . global_args [ for_machine ]
2018-07-21 04:32:55 +08:00
return d . get ( compiler . get_language ( ) , [ ] )
2013-09-24 03:34:41 +08:00
2018-10-05 08:52:08 +08:00
def get_project_args ( self , compiler , project , for_machine ) :
d = self . projects_args [ for_machine ]
2018-07-21 04:32:55 +08:00
args = d . get ( project )
2016-11-12 00:27:56 +08:00
if not args :
return [ ]
return args . get ( compiler . get_language ( ) , [ ] )
2018-10-05 08:52:08 +08:00
def get_global_link_args ( self , compiler , for_machine ) :
d = self . global_link_args [ for_machine ]
2018-07-21 04:32:55 +08:00
return d . get ( compiler . get_language ( ) , [ ] )
2016-05-29 02:31:59 +08:00
2018-10-05 08:52:08 +08:00
def get_project_link_args ( self , compiler , project , for_machine ) :
d = self . projects_link_args [ for_machine ]
2018-07-21 04:32:55 +08:00
link_args = d . get ( project )
2016-11-12 00:27:56 +08:00
if not link_args :
return [ ]
return link_args . get ( compiler . get_language ( ) , [ ] )
2017-01-17 21:13:03 +08:00
class IncludeDirs :
2016-01-03 03:34:39 +08:00
def __init__ ( self , curdir , dirs , is_system , extra_build_dirs = None ) :
2013-09-24 04:08:50 +08:00
self . curdir = curdir
self . incdirs = dirs
2016-01-03 03:34:39 +08:00
self . is_system = is_system
2014-01-19 05:11:59 +08:00
# Interpreter has validated that all given directories
# actually exist.
2015-10-04 04:18:40 +08:00
if extra_build_dirs is None :
self . extra_build_dirs = [ ]
else :
self . extra_build_dirs = extra_build_dirs
2013-09-24 04:08:50 +08:00
2017-01-21 15:12:26 +08:00
def __repr__ ( self ) :
r = ' < {} {} / {} > '
return r . format ( self . __class__ . __name__ , self . curdir , self . incdirs )
2013-09-24 04:08:50 +08:00
def get_curdir ( self ) :
return self . curdir
def get_incdirs ( self ) :
return self . incdirs
2015-10-04 04:18:40 +08:00
def get_extra_build_dirs ( self ) :
return self . extra_build_dirs
2017-01-17 21:13:03 +08:00
class ExtractedObjects :
2016-10-21 10:08:32 +08:00
'''
Holds a list of sources for which the objects must be extracted
'''
2019-04-23 05:26:18 +08:00
def __init__ ( self , target , srclist = None , genlist = None , objlist = None , recursive = True ) :
2013-11-05 07:47:09 +08:00
self . target = target
2018-04-17 09:29:21 +08:00
self . recursive = recursive
2019-04-23 05:26:18 +08:00
self . srclist = srclist if srclist is not None else [ ]
self . genlist = genlist if genlist is not None else [ ]
self . objlist = objlist if objlist is not None else [ ]
2018-04-14 03:21:57 +08:00
if self . target . is_unity :
2016-11-24 06:21:11 +08:00
self . check_unity_compatible ( )
2016-10-21 10:08:32 +08:00
2016-12-09 15:28:22 +08:00
def __repr__ ( self ) :
r = ' < {0} {1!r} : {2} > '
return r . format ( self . __class__ . __name__ , self . target . name , self . srclist )
2018-04-14 03:21:57 +08:00
def classify_all_sources ( self , sources , generated_sources ) :
# Merge sources and generated sources
sources = list ( sources )
for gensrc in generated_sources :
for s in gensrc . get_outputs ( ) :
# We cannot know the path where this source will be generated,
# but all we need here is the file extension to determine the
# compiler.
sources . append ( s )
# Filter out headers and all non-source files
sources = [ s for s in sources if environment . is_source ( s ) and not environment . is_header ( s ) ]
return classify_unity_sources ( self . target . compilers . values ( ) , sources )
2016-10-21 10:08:32 +08:00
def check_unity_compatible ( self ) :
# Figure out if the extracted object list is compatible with a Unity
# build. When we're doing a Unified build, we go through the sources,
# and create a single source file from each subset of the sources that
# can be compiled with a specific compiler. Then we create one object
2018-04-14 03:21:57 +08:00
# from each unified source file. So for each compiler we can either
# extra all its sources or none.
cmpsrcs = self . classify_all_sources ( self . target . sources , self . target . generated )
extracted_cmpsrcs = self . classify_all_sources ( self . srclist , self . genlist )
2016-10-21 10:08:32 +08:00
2018-04-14 03:21:57 +08:00
for comp , srcs in extracted_cmpsrcs . items ( ) :
if set ( srcs ) != set ( cmpsrcs [ comp ] ) :
raise MesonException ( ' Single object files can not be extracted '
' in Unity builds. You can only extract all '
' the object files for each compiler at once. ' )
2013-11-05 07:47:09 +08:00
2019-08-18 02:12:56 +08:00
def get_outputs ( self , backend ) :
# TODO: Consider if we need to handle genlist here
return [
backend . object_filename_from_source ( self . target , source )
for source in self . srclist
]
2017-01-17 21:13:03 +08:00
class EnvironmentVariables :
2016-09-15 04:11:27 +08:00
def __init__ ( self ) :
self . envvars = [ ]
2019-04-13 13:55:58 +08:00
# The set of all env vars we have operations for. Only used for self.has_name()
self . varnames = set ( )
2016-09-15 04:11:27 +08:00
2017-02-19 08:11:38 +08:00
def __repr__ ( self ) :
repr_str = " < {0} : {1} > "
return repr_str . format ( self . __class__ . __name__ , self . envvars )
2019-04-13 13:55:58 +08:00
def add_var ( self , method , name , args , kwargs ) :
self . varnames . add ( name )
self . envvars . append ( ( method , name , args , kwargs ) )
def has_name ( self , name ) :
return name in self . varnames
2017-05-17 16:41:54 +08:00
def get_value ( self , values , kwargs ) :
2016-09-15 04:11:27 +08:00
separator = kwargs . get ( ' separator ' , os . pathsep )
value = ' '
for var in values :
value + = separator + var
return separator , value . strip ( separator )
def set ( self , env , name , values , kwargs ) :
2017-05-17 16:41:54 +08:00
return self . get_value ( values , kwargs ) [ 1 ]
2016-09-15 04:11:27 +08:00
def append ( self , env , name , values , kwargs ) :
2017-05-17 16:41:54 +08:00
sep , value = self . get_value ( values , kwargs )
2016-09-15 04:11:27 +08:00
if name in env :
return env [ name ] + sep + value
return value
def prepend ( self , env , name , values , kwargs ) :
2017-05-17 16:41:54 +08:00
sep , value = self . get_value ( values , kwargs )
2016-09-15 04:11:27 +08:00
if name in env :
2017-01-01 03:09:41 +08:00
return value + sep + env [ name ]
2016-09-15 04:11:27 +08:00
return value
2020-01-06 22:27:38 +08:00
def get_env ( self , full_env : T . Dict [ str , str ] ) - > T . Dict [ str , str ] :
2018-05-10 00:54:49 +08:00
env = full_env . copy ( )
2016-09-15 04:11:27 +08:00
for method , name , values , kwargs in self . envvars :
env [ name ] = method ( full_env , name , values , kwargs )
return env
2017-01-14 22:46:37 +08:00
class Target :
2021-01-13 03:20:13 +08:00
# TODO: should Target be an abc.ABCMeta?
2020-12-05 09:01:45 +08:00
def __init__ ( self , name : str , subdir : str , subproject : str , build_by_default : bool , for_machine : MachineChoice ) :
2017-05-29 22:59:28 +08:00
if has_path_sep ( name ) :
2017-06-01 20:16:22 +08:00
# Fix failing test 53 when this becomes an error.
2020-03-04 03:45:43 +08:00
mlog . warning ( ''' Target " {} " has a path separator in its name.
2017-06-01 20:16:22 +08:00
This is not supported , it can cause unexpected failures and will become
2020-03-04 03:45:43 +08:00
a hard error in the future . ''' .format(name))
2013-09-24 03:34:41 +08:00
self . name = name
self . subdir = subdir
2017-09-07 06:21:50 +08:00
self . subproject = subproject
2017-01-15 20:51:34 +08:00
self . build_by_default = build_by_default
2018-10-05 08:52:08 +08:00
self . for_machine = for_machine
2017-01-15 08:01:21 +08:00
self . install = False
2018-03-10 01:42:44 +08:00
self . build_always_stale = False
2020-12-05 08:09:10 +08:00
self . option_overrides_base : T . Dict [ OptionKey , str ] = { }
2020-12-05 09:01:45 +08:00
self . option_overrides_compiler : T . Dict [ OptionKey , str ] = { }
2020-10-14 01:44:41 +08:00
self . extra_files = [ ] # type: T.List[File]
2018-12-30 18:31:03 +08:00
if not hasattr ( self , ' typename ' ) :
raise RuntimeError ( ' Target type is not set for target class " {} " . This is a bug ' . format ( type ( self ) . __name__ ) )
2017-01-14 22:46:37 +08:00
2020-09-03 17:42:53 +08:00
def __lt__ ( self , other : object ) - > bool :
2019-12-06 00:24:56 +08:00
if not hasattr ( other , ' get_id ' ) and not callable ( other . get_id ) :
return NotImplemented
2019-12-05 22:46:39 +08:00
return self . get_id ( ) < other . get_id ( )
2020-09-03 17:42:53 +08:00
def __le__ ( self , other : object ) - > bool :
2019-12-06 00:24:56 +08:00
if not hasattr ( other , ' get_id ' ) and not callable ( other . get_id ) :
return NotImplemented
2019-12-05 22:46:39 +08:00
return self . get_id ( ) < = other . get_id ( )
2020-09-03 17:42:53 +08:00
def __gt__ ( self , other : object ) - > bool :
2019-12-06 00:24:56 +08:00
if not hasattr ( other , ' get_id ' ) and not callable ( other . get_id ) :
return NotImplemented
2019-12-05 22:46:39 +08:00
return self . get_id ( ) > other . get_id ( )
2020-09-03 17:42:53 +08:00
def __ge__ ( self , other : object ) - > bool :
2019-12-06 00:24:56 +08:00
if not hasattr ( other , ' get_id ' ) and not callable ( other . get_id ) :
return NotImplemented
2019-12-05 22:46:39 +08:00
return self . get_id ( ) > = other . get_id ( )
2021-01-13 03:20:13 +08:00
def get_default_install_dir ( self , env : environment . Environment ) - > str :
raise NotImplementedError
def get_install_dir ( self , environment : environment . Environment ) - > T . Tuple [ T . Any , bool ] :
2018-09-30 02:54:42 +08:00
# Find the installation directory.
default_install_dir = self . get_default_install_dir ( environment )
outdirs = self . get_custom_install_dir ( )
if outdirs [ 0 ] is not None and outdirs [ 0 ] != default_install_dir and outdirs [ 0 ] is not True :
# Either the value is set to a non-default value, or is set to
# False (which means we want this specific output out of many
# outputs to not be installed).
custom_install_dir = True
else :
custom_install_dir = False
outdirs [ 0 ] = default_install_dir
return outdirs , custom_install_dir
2020-09-01 20:28:08 +08:00
def get_basename ( self ) - > str :
2017-01-14 22:46:37 +08:00
return self . name
2020-09-01 20:28:08 +08:00
def get_subdir ( self ) - > str :
2017-01-14 22:46:37 +08:00
return self . subdir
2020-09-01 20:28:08 +08:00
def get_typename ( self ) - > str :
2018-11-29 02:40:16 +08:00
return self . typename
2018-09-26 01:07:09 +08:00
@staticmethod
def _get_id_hash ( target_id ) :
# We don't really need cryptographic security here.
# Small-digest hash function with unlikely collision is good enough.
h = hashlib . sha256 ( )
h . update ( target_id . encode ( encoding = ' utf-8 ' , errors = ' replace ' ) )
# This ID should be case-insensitive and should work in Visual Studio,
# e.g. it should not start with leading '-'.
return h . hexdigest ( ) [ : 7 ]
@staticmethod
2020-09-01 20:28:08 +08:00
def construct_id_from_path ( subdir : str , name : str , type_suffix : str ) - > str :
2018-09-26 01:07:09 +08:00
""" Construct target ID from subdir, name and type suffix.
This helper function is made public mostly for tests . """
2017-09-07 06:21:50 +08:00
# This ID must also be a valid file name on all OSs.
# It should also avoid shell metacharacters for obvious
2018-03-14 01:56:33 +08:00
# reasons. '@' is not used as often as '_' in source code names.
# In case of collisions consider using checksums.
# FIXME replace with assert when slash in names is prohibited
2018-09-26 01:07:09 +08:00
name_part = name . replace ( ' / ' , ' @ ' ) . replace ( ' \\ ' , ' @ ' )
assert not has_path_sep ( type_suffix )
my_id = name_part + type_suffix
if subdir :
subdir_part = Target . _get_id_hash ( subdir )
# preserve myid for better debuggability
return subdir_part + ' @@ ' + my_id
return my_id
2020-09-01 20:28:08 +08:00
def get_id ( self ) - > str :
2018-09-26 01:07:09 +08:00
return self . construct_id_from_path (
self . subdir , self . name , self . type_suffix ( ) )
2017-09-07 06:21:50 +08:00
2020-12-05 08:09:10 +08:00
def process_kwargs_base ( self , kwargs : T . Dict [ str , T . Any ] ) - > None :
2017-01-15 20:51:34 +08:00
if ' build_by_default ' in kwargs :
self . build_by_default = kwargs [ ' build_by_default ' ]
if not isinstance ( self . build_by_default , bool ) :
raise InvalidArguments ( ' build_by_default must be a boolean value. ' )
2018-09-30 20:53:12 +08:00
elif kwargs . get ( ' install ' , False ) :
# For backward compatibility, if build_by_default is not explicitly
# set, use the value of 'install' if it's enabled.
self . build_by_default = True
2019-06-13 06:08:45 +08:00
option_overrides = self . parse_overrides ( kwargs )
for k , v in option_overrides . items ( ) :
2020-12-05 08:09:10 +08:00
if k . lang :
self . option_overrides_compiler [ k . evolve ( machine = self . for_machine ) ] = v
continue
2019-06-13 06:08:45 +08:00
self . option_overrides_base [ k ] = v
2017-03-13 02:05:21 +08:00
2020-12-05 08:09:10 +08:00
@staticmethod
def parse_overrides ( kwargs : T . Dict [ str , T . Any ] ) - > T . Dict [ OptionKey , str ] :
result : T . Dict [ OptionKey , str ] = { }
2017-03-13 02:05:21 +08:00
overrides = stringlistify ( kwargs . get ( ' override_options ' , [ ] ) )
for o in overrides :
if ' = ' not in o :
raise InvalidArguments ( ' Overrides must be of form " key=value " ' )
k , v = o . split ( ' = ' , 1 )
2020-12-05 08:09:10 +08:00
key = OptionKey . from_string ( k . strip ( ) )
2017-03-13 02:05:21 +08:00
v = v . strip ( )
2020-12-05 08:09:10 +08:00
result [ key ] = v
2017-03-13 02:05:21 +08:00
return result
2019-05-03 04:26:51 +08:00
def is_linkable_target ( self ) - > bool :
2018-09-13 23:57:34 +08:00
return False
2017-01-14 22:46:37 +08:00
2020-09-01 20:28:08 +08:00
def get_outputs ( self ) - > T . List [ str ] :
return [ ]
def should_install ( self ) - > bool :
return False
2017-01-14 22:46:37 +08:00
class BuildTarget ( Target ) :
2018-03-30 01:36:46 +08:00
known_kwargs = known_build_target_kwargs
2020-10-22 07:00:17 +08:00
def __init__ ( self , name : str , subdir : str , subproject : str , for_machine : MachineChoice ,
sources : T . List [ File ] , objects , environment : environment . Environment , kwargs ) :
2018-10-05 08:52:08 +08:00
super ( ) . __init__ ( name , subdir , subproject , True , for_machine )
2020-12-05 09:01:45 +08:00
unity_opt = environment . coredata . get_option ( OptionKey ( ' unity ' ) )
2017-03-14 02:13:21 +08:00
self . is_unity = unity_opt == ' on ' or ( unity_opt == ' subprojects ' and subproject != ' ' )
2016-09-25 13:46:57 +08:00
self . environment = environment
2021-01-06 07:55:02 +08:00
self . sources : T . List [ File ] = [ ]
2019-02-25 15:45:20 +08:00
self . compilers = OrderedDict ( ) # type: OrderedDict[str, Compiler]
2013-11-05 06:16:17 +08:00
self . objects = [ ]
2013-09-24 03:34:41 +08:00
self . external_deps = [ ]
self . include_dirs = [ ]
2019-05-03 04:26:51 +08:00
self . link_language = kwargs . get ( ' link_language ' )
2021-01-13 03:20:13 +08:00
self . link_targets : T . List [ BuildTarget ] = [ ]
2017-04-03 00:06:16 +08:00
self . link_whole_targets = [ ]
2014-03-08 02:10:59 +08:00
self . link_depends = [ ]
2020-05-13 20:02:29 +08:00
self . added_deps = set ( )
2016-10-12 14:42:27 +08:00
self . name_prefix_set = False
self . name_suffix_set = False
2013-09-24 03:34:41 +08:00
self . filename = ' no_name '
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
# The list of all files outputted by this target. Useful in cases such
# as Vala which generates .vapi and .h besides the compiled output.
self . outputs = [ self . filename ]
2013-09-24 03:34:41 +08:00
self . need_install = False
self . pch = { }
2020-10-22 07:00:17 +08:00
self . extra_args : T . Dict [ str , T . List [ ' FileOrString ' ] ] = { }
2021-01-06 07:55:02 +08:00
self . generated : T . Sequence [ T . Union [ GeneratedList , CustomTarget , CustomTargetIndex ] ] = [ ]
2018-02-25 21:49:58 +08:00
self . d_features = { }
2018-10-02 08:48:34 +08:00
self . pic = False
self . pie = False
2020-05-08 01:15:06 +08:00
# Track build_rpath entries so we can remove them at install time
2021-01-13 03:20:13 +08:00
self . rpath_dirs_to_remove : T . Set [ bytes ] = set ( )
2016-12-09 15:19:56 +08:00
# Sources can be:
# 1. Pre-existing source files in the source tree
# 2. Pre-existing sources generated by configure_file in the build tree
# 3. Sources files generated by another target or a Generator
2013-09-24 03:34:41 +08:00
self . process_sourcelist ( sources )
2016-12-09 15:19:56 +08:00
# Objects can be:
# 1. Pre-existing objects provided by the user with the `objects:` kwarg
# 2. Compiled objects created by and extracted from another target
2013-11-05 06:16:17 +08:00
self . process_objectlist ( objects )
2014-04-26 03:13:15 +08:00
self . process_kwargs ( kwargs , environment )
2014-09-14 18:47:18 +08:00
self . check_unknown_kwargs ( kwargs )
2018-02-13 06:00:11 +08:00
self . process_compilers ( )
2017-09-13 05:53:19 +08:00
if not any ( [ self . sources , self . generated , self . objects , self . link_whole ] ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' Build target {} has no sources. ' . format ( name ) )
2017-09-16 07:29:59 +08:00
self . process_compilers_late ( )
2014-09-14 18:47:18 +08:00
self . validate_sources ( )
2018-10-05 08:52:08 +08:00
self . validate_install ( environment )
2018-03-21 16:49:36 +08:00
self . check_module_linking ( )
2014-09-14 18:47:18 +08:00
2016-07-01 17:13:51 +08:00
def __repr__ ( self ) :
repr_str = " < {0} {1} : {2} > "
return repr_str . format ( self . __class__ . __name__ , self . get_id ( ) , self . filename )
2020-02-13 02:47:51 +08:00
def __str__ ( self ) :
return " {} " . format ( self . name )
2018-10-05 08:52:08 +08:00
def validate_install ( self , environment ) :
if self . for_machine is MachineChoice . BUILD and self . need_install :
if environment . is_cross_build ( ) :
raise InvalidArguments ( ' Tried to install a target for the build machine in a cross build. ' )
else :
mlog . warning ( ' Installing target build for the build machine. This will fail in a cross build. ' )
2017-02-26 20:18:57 +08:00
2014-09-14 18:47:18 +08:00
def check_unknown_kwargs ( self , kwargs ) :
# Override this method in derived classes that have more
# keywords.
2018-03-30 01:36:46 +08:00
self . check_unknown_kwargs_int ( kwargs , self . known_kwargs )
2014-09-14 18:47:18 +08:00
def check_unknown_kwargs_int ( self , kwargs , known_kwargs ) :
2014-08-13 03:44:16 +08:00
unknowns = [ ]
for k in kwargs :
2016-12-20 04:48:35 +08:00
if k not in known_kwargs :
2014-08-13 03:44:16 +08:00
unknowns . append ( k )
if len ( unknowns ) > 0 :
2020-03-04 03:45:43 +08:00
mlog . warning ( ' Unknown keyword argument(s) in target {} : {} . ' . format ( self . name , ' , ' . join ( unknowns ) ) )
2013-10-05 04:04:26 +08:00
2013-11-05 06:16:17 +08:00
def process_objectlist ( self , objects ) :
assert ( isinstance ( objects , list ) )
2020-03-06 01:50:30 +08:00
for s in unholder ( objects ) :
2016-12-09 16:06:13 +08:00
if isinstance ( s , ( str , File , ExtractedObjects ) ) :
2013-11-05 06:16:17 +08:00
self . objects . append ( s )
2016-08-27 20:47:29 +08:00
elif isinstance ( s , ( GeneratedList , CustomTarget ) ) :
2017-01-01 03:04:39 +08:00
msg = ' Generated files are not allowed in the \' objects \' kwarg ' + \
2016-08-16 02:45:37 +08:00
' for target {!r} . \n It is meant only for ' . format ( self . name ) + \
' pre-built object files that are shipped with the \n source ' + \
' tree. Try adding it in the list of sources. '
raise InvalidArguments ( msg )
2013-11-05 06:16:17 +08:00
else :
2016-08-16 02:45:37 +08:00
msg = ' Bad object of type {!r} in target {!r} . ' . format ( type ( s ) . __name__ , self . name )
raise InvalidArguments ( msg )
2013-11-05 06:16:17 +08:00
2013-09-24 03:34:41 +08:00
def process_sourcelist ( self , sources ) :
2017-08-29 17:20:25 +08:00
sources = listify ( sources )
2015-03-14 06:49:10 +08:00
added_sources = { } # If the same source is defined multiple times, use it only once.
2020-03-06 01:50:30 +08:00
for s in unholder ( sources ) :
2015-04-25 23:11:17 +08:00
if isinstance ( s , File ) :
2016-12-20 04:48:35 +08:00
if s not in added_sources :
2015-03-14 06:49:10 +08:00
self . sources . append ( s )
added_sources [ s ] = True
2017-09-26 04:46:07 +08:00
elif isinstance ( s , ( GeneratedList , CustomTarget , CustomTargetIndex ) ) :
2013-09-24 03:34:41 +08:00
self . generated . append ( s )
else :
2016-08-16 02:45:37 +08:00
msg = ' Bad source of type {!r} in target {!r} . ' . format ( type ( s ) . __name__ , self . name )
raise InvalidArguments ( msg )
2013-09-24 03:34:41 +08:00
2016-09-25 13:46:57 +08:00
@staticmethod
def can_compile_remove_sources ( compiler , sources ) :
removed = False
for s in sources [ : ] :
if compiler . can_compile ( s ) :
sources . remove ( s )
removed = True
return removed
2017-09-16 07:29:59 +08:00
def process_compilers_late ( self ) :
""" Processes additional compilers after kwargs have been evaluated.
This can add extra compilers that might be required by keyword
arguments , such as link_with or dependencies . It will also try to guess
which compiler to use if one hasn ' t been selected already.
"""
# Populate list of compilers
2018-10-05 08:52:08 +08:00
compilers = self . environment . coredata . compilers [ self . for_machine ]
2017-09-16 07:29:59 +08:00
2019-05-03 04:26:51 +08:00
# did user override clink_langs for this target?
link_langs = [ self . link_language ] if self . link_language else clink_langs
2017-09-16 07:29:59 +08:00
# If this library is linked against another library we need to consider
# the languages of those libraries as well.
if self . link_targets or self . link_whole_targets :
extra = set ( )
for t in itertools . chain ( self . link_targets , self . link_whole_targets ) :
2019-03-28 00:18:39 +08:00
if isinstance ( t , CustomTarget ) or isinstance ( t , CustomTargetIndex ) :
2019-03-18 04:34:19 +08:00
continue # We can't know anything about these.
2017-09-16 07:29:59 +08:00
for name , compiler in t . compilers . items ( ) :
2019-05-03 04:26:51 +08:00
if name in link_langs :
2017-09-16 07:29:59 +08:00
extra . add ( ( name , compiler ) )
2018-06-19 12:17:25 +08:00
for name , compiler in sorted ( extra , key = lambda p : sort_clink ( p [ 0 ] ) ) :
2017-09-16 07:29:59 +08:00
self . compilers [ name ] = compiler
if not self . compilers :
# No source files or parent targets, target consists of only object
2018-06-19 12:17:25 +08:00
# files of unknown origin. Just add the first clink compiler
2017-09-16 07:29:59 +08:00
# that we have and hope that it can link these objects
2019-05-03 04:26:51 +08:00
for lang in link_langs :
2017-09-16 07:29:59 +08:00
if lang in compilers :
self . compilers [ lang ] = compilers [ lang ]
break
2016-09-25 13:46:57 +08:00
def process_compilers ( self ) :
2016-12-09 15:19:56 +08:00
'''
Populate self . compilers , which is the list of compilers that this
target will use for compiling all its sources .
We also add compilers that were used by extracted objects to simplify
dynamic linker determination .
'''
Don't use len() to test emptiness vs not emptiness
Meson has a common pattern of using 'if len(foo) == 0:' or
'if len(foo) != 0:', however, this is a common anti-pattern in python.
Instead tests for emptiness/non-emptiness should be done with a simple
'if foo:' or 'if not foo:'
Consider the following:
>>> import timeit
>>> timeit.timeit('if len([]) == 0: pass')
0.10730923599840025
>>> timeit.timeit('if not []: pass')
0.030033907998586074
>>> timeit.timeit('if len(['a', 'b', 'c', 'd']) == 0: pass')
0.1154778649979562
>>> timeit.timeit("if not ['a', 'b', 'c', 'd']: pass")
0.08259823200205574
>>> timeit.timeit('if len("") == 0: pass')
0.089759664999292
>>> timeit.timeit('if not "": pass')
0.02340641999762738
>>> timeit.timeit('if len("foo") == 0: pass')
0.08848102600313723
>>> timeit.timeit('if not "foo": pass')
0.04032287199879647
And for the one additional case of 'if len(foo.strip()) == 0', which can
be replaced with 'if not foo.isspace()'
>>> timeit.timeit('if len(" ".strip()) == 0: pass')
0.15294511600222904
>>> timeit.timeit('if " ".isspace(): pass')
0.09413968399894657
>>> timeit.timeit('if len(" abc".strip()) == 0: pass')
0.2023209120015963
>>> timeit.timeit('if " abc".isspace(): pass')
0.09571301700270851
In other words, it's always a win to not use len(), when you don't
actually want to check the length.
2017-05-02 06:11:01 +08:00
if not self . sources and not self . generated and not self . objects :
2016-09-25 13:46:57 +08:00
return
2016-10-21 09:32:25 +08:00
# Populate list of compilers
2018-10-05 08:52:08 +08:00
compilers = self . environment . coredata . compilers [ self . for_machine ]
2016-12-09 15:19:56 +08:00
# Pre-existing sources
sources = list ( self . sources )
# All generated sources
for gensrc in self . generated :
for s in gensrc . get_outputs ( ) :
# Generated objects can't be compiled, so don't use them for
# compiler detection. If our target only has generated objects,
# we will fall back to using the first c-like compiler we find,
# which is what we need.
if not is_object ( s ) :
sources . append ( s )
2020-03-06 01:50:30 +08:00
for d in unholder ( self . external_deps ) :
2018-02-13 06:00:11 +08:00
for s in d . sources :
if isinstance ( s , ( str , File ) ) :
sources . append ( s )
2016-12-09 15:19:56 +08:00
# Sources that were used to create our extracted objects
for o in self . objects :
if not isinstance ( o , ExtractedObjects ) :
continue
for s in o . srclist :
# Don't add Vala sources since that will pull in the Vala
# compiler even though we will never use it since we are
# dealing with compiled C code.
if not s . endswith ( lang_suffixes [ ' vala ' ] ) :
sources . append ( s )
if sources :
2017-03-24 21:01:22 +08:00
# For each source, try to add one compiler that can compile it.
2020-02-10 02:49:03 +08:00
#
# If it has a suffix that belongs to a known language, we must have
# a compiler for that language.
#
# Otherwise, it's ok if no compilers can compile it, because users
# are expected to be able to add arbitrary non-source files to the
# sources list
2017-03-24 21:01:22 +08:00
for s in sources :
for lang , compiler in compilers . items ( ) :
if compiler . can_compile ( s ) :
if lang not in self . compilers :
self . compilers [ lang ] = compiler
break
2020-02-10 02:49:03 +08:00
else :
if is_known_suffix ( s ) :
raise MesonException ( ' No {} machine compiler for " {} " ' .
format ( self . for_machine . get_lower_case_name ( ) , s ) )
2018-06-19 12:17:25 +08:00
# Re-sort according to clink_langs
2017-03-27 17:10:34 +08:00
self . compilers = OrderedDict ( sorted ( self . compilers . items ( ) ,
2018-06-19 12:17:25 +08:00
key = lambda t : sort_clink ( t [ 0 ] ) ) )
2017-09-16 07:29:59 +08:00
2016-10-21 09:32:25 +08:00
# If all our sources are Vala, our target also needs the C compiler but
# it won't get added above.
if ' vala ' in self . compilers and ' c ' not in self . compilers :
self . compilers [ ' c ' ] = compilers [ ' c ' ]
2016-09-25 13:46:57 +08:00
2014-06-23 00:38:47 +08:00
def validate_sources ( self ) :
Don't use len() to test emptiness vs not emptiness
Meson has a common pattern of using 'if len(foo) == 0:' or
'if len(foo) != 0:', however, this is a common anti-pattern in python.
Instead tests for emptiness/non-emptiness should be done with a simple
'if foo:' or 'if not foo:'
Consider the following:
>>> import timeit
>>> timeit.timeit('if len([]) == 0: pass')
0.10730923599840025
>>> timeit.timeit('if not []: pass')
0.030033907998586074
>>> timeit.timeit('if len(['a', 'b', 'c', 'd']) == 0: pass')
0.1154778649979562
>>> timeit.timeit("if not ['a', 'b', 'c', 'd']: pass")
0.08259823200205574
>>> timeit.timeit('if len("") == 0: pass')
0.089759664999292
>>> timeit.timeit('if not "": pass')
0.02340641999762738
>>> timeit.timeit('if len("foo") == 0: pass')
0.08848102600313723
>>> timeit.timeit('if not "foo": pass')
0.04032287199879647
And for the one additional case of 'if len(foo.strip()) == 0', which can
be replaced with 'if not foo.isspace()'
>>> timeit.timeit('if len(" ".strip()) == 0: pass')
0.15294511600222904
>>> timeit.timeit('if " ".isspace(): pass')
0.09413968399894657
>>> timeit.timeit('if len(" abc".strip()) == 0: pass')
0.2023209120015963
>>> timeit.timeit('if " abc".isspace(): pass')
0.09571301700270851
In other words, it's always a win to not use len(), when you don't
actually want to check the length.
2017-05-02 06:11:01 +08:00
if not self . sources :
2016-09-25 13:55:02 +08:00
return
for lang in ( ' cs ' , ' java ' ) :
if lang in self . compilers :
check_sources = list ( self . sources )
compiler = self . compilers [ lang ]
if not self . can_compile_remove_sources ( compiler , check_sources ) :
m = ' No {} sources found in target {!r} ' . format ( lang , self . name )
raise InvalidArguments ( m )
if check_sources :
m = ' {0} targets can only contain {0} files: \n ' . format ( lang . capitalize ( ) )
m + = ' \n ' . join ( [ repr ( c ) for c in check_sources ] )
raise InvalidArguments ( m )
# CSharp and Java targets can't contain any other file types
assert ( len ( self . compilers ) == 1 )
return
2014-06-23 00:38:47 +08:00
2017-04-20 06:32:24 +08:00
def process_link_depends ( self , sources , environment ) :
""" Process the link_depends keyword argument.
This is designed to handle strings , Files , and the output of Custom
Targets . Notably it doesn ' t handle generator() returned objects, since
adding them as a link depends would inherently cause them to be
generated twice , since the output needs to be passed to the ld_args and
link_depends .
"""
2017-08-29 17:20:25 +08:00
sources = listify ( sources )
2020-03-06 01:50:30 +08:00
for s in unholder ( sources ) :
2017-04-20 06:32:24 +08:00
if isinstance ( s , File ) :
self . link_depends . append ( s )
elif isinstance ( s , str ) :
self . link_depends . append (
File . from_source_file ( environment . source_dir , self . subdir , s ) )
elif hasattr ( s , ' get_outputs ' ) :
self . link_depends . extend (
2020-05-08 19:34:49 +08:00
[ File . from_built_file ( s . get_subdir ( ) , p ) for p in s . get_outputs ( ) ] )
2017-04-20 06:32:24 +08:00
else :
raise InvalidArguments (
' Link_depends arguments must be strings, Files, '
' or a Custom Target, or lists thereof. ' )
2013-09-24 03:34:41 +08:00
def get_original_kwargs ( self ) :
return self . kwargs
2013-11-05 07:47:09 +08:00
def unpack_holder ( self , d ) :
2017-08-29 17:20:25 +08:00
d = listify ( d )
2013-11-05 07:47:09 +08:00
newd = [ ]
for i in d :
2017-07-01 07:10:02 +08:00
if isinstance ( i , list ) :
i = self . unpack_holder ( i )
elif hasattr ( i , ' held_object ' ) :
i = i . held_object
for t in [ ' dependencies ' , ' link_with ' , ' include_directories ' , ' sources ' ] :
if hasattr ( i , t ) :
setattr ( i , t , self . unpack_holder ( getattr ( i , t ) ) )
newd . append ( i )
2013-11-05 07:47:09 +08:00
return newd
2013-09-29 00:50:15 +08:00
def copy_kwargs ( self , kwargs ) :
2013-09-24 03:34:41 +08:00
self . kwargs = copy . copy ( kwargs )
2013-09-29 00:50:15 +08:00
# This sucks quite badly. Arguments
# are holders but they can't be pickled
# so unpack those known.
2017-07-01 07:10:02 +08:00
for k , v in self . kwargs . items ( ) :
if isinstance ( v , list ) :
self . kwargs [ k ] = self . unpack_holder ( v )
if hasattr ( v , ' held_object ' ) :
self . kwargs [ k ] = v . held_object
for t in [ ' dependencies ' , ' link_with ' , ' include_directories ' , ' sources ' ] :
if t in self . kwargs :
self . kwargs [ t ] = self . unpack_holder ( self . kwargs [ t ] )
2013-11-05 07:47:09 +08:00
2016-10-21 10:08:32 +08:00
def extract_objects ( self , srclist ) :
2013-11-05 07:47:09 +08:00
obj_src = [ ]
2020-08-17 21:27:13 +08:00
sources_set = set ( self . sources )
2016-10-21 10:08:32 +08:00
for src in srclist :
2019-02-19 08:10:35 +08:00
if isinstance ( src , str ) :
src = File ( False , self . subdir , src )
elif isinstance ( src , File ) :
2020-05-13 01:53:37 +08:00
FeatureNew . single_use ( ' File argument for extract_objects ' , ' 0.50.0 ' , self . subproject )
2019-02-19 08:10:35 +08:00
else :
raise MesonException ( ' Object extraction arguments must be strings or Files. ' )
2018-01-06 04:53:13 +08:00
# FIXME: It could be a generated source
2020-08-17 21:27:13 +08:00
if src not in sources_set :
2020-03-04 03:45:43 +08:00
raise MesonException ( ' Tried to extract unknown source {} . ' . format ( src ) )
2016-10-21 10:08:32 +08:00
obj_src . append ( src )
2018-04-13 22:33:42 +08:00
return ExtractedObjects ( self , obj_src )
2013-09-29 00:50:15 +08:00
2018-04-17 09:29:21 +08:00
def extract_all_objects ( self , recursive = True ) :
return ExtractedObjects ( self , self . sources , self . generated , self . objects ,
recursive )
2015-07-22 02:34:18 +08:00
2015-09-03 05:01:39 +08:00
def get_all_link_deps ( self ) :
return self . get_transitive_link_deps ( )
2013-10-05 04:04:26 +08:00
2018-09-09 23:21:58 +08:00
@lru_cache ( maxsize = None )
2015-09-03 05:01:39 +08:00
def get_transitive_link_deps ( self ) :
2013-10-05 04:04:26 +08:00
result = [ ]
for i in self . link_targets :
2015-09-03 05:01:39 +08:00
result + = i . get_all_link_deps ( )
2013-10-05 04:04:26 +08:00
return result
2014-05-22 04:47:23 +08:00
2018-09-30 03:15:00 +08:00
def get_link_deps_mapping ( self , prefix , environment ) :
return self . get_transitive_link_deps_mapping ( prefix , environment )
@lru_cache ( maxsize = None )
def get_transitive_link_deps_mapping ( self , prefix , environment ) :
result = { }
for i in self . link_targets :
mapping = i . get_link_deps_mapping ( prefix , environment )
#we are merging two dictionaries, while keeping the earlier one dominant
result_tmp = mapping . copy ( )
result_tmp . update ( result )
result = result_tmp
return result
2018-09-10 15:14:37 +08:00
@lru_cache ( maxsize = None )
def get_link_dep_subdirs ( self ) :
result = OrderedSet ( )
for i in self . link_targets :
2020-07-19 23:44:18 +08:00
if not isinstance ( i , StaticLibrary ) :
result . add ( i . get_subdir ( ) )
2018-09-10 15:14:37 +08:00
result . update ( i . get_link_dep_subdirs ( ) )
return result
2021-01-13 03:20:13 +08:00
def get_default_install_dir ( self , environment : environment . Environment ) - > str :
2018-09-30 02:54:42 +08:00
return environment . get_libdir ( )
2014-02-13 04:37:25 +08:00
def get_custom_install_dir ( self ) :
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
return self . install_dir
2013-10-05 04:04:26 +08:00
2018-04-25 12:39:59 +08:00
def get_custom_install_mode ( self ) :
return self . install_mode
2014-04-26 03:13:15 +08:00
def process_kwargs ( self , kwargs , environment ) :
2019-12-04 22:46:03 +08:00
self . process_kwargs_base ( kwargs )
2013-09-29 00:50:15 +08:00
self . copy_kwargs ( kwargs )
2013-09-24 03:34:41 +08:00
kwargs . get ( ' modules ' , [ ] )
self . need_install = kwargs . get ( ' install ' , self . need_install )
2017-08-30 00:36:40 +08:00
llist = extract_as_list ( kwargs , ' link_with ' )
2020-03-06 01:50:30 +08:00
for linktarget in unholder ( llist ) :
2017-10-08 22:11:29 +08:00
if isinstance ( linktarget , dependencies . ExternalLibrary ) :
raise MesonException ( ''' An external library was used in link_with keyword argument, which
is reserved for libraries built as part of this project . External
libraries must be passed using the dependencies keyword argument
instead , because they are conceptually " external dependencies " ,
just like those detected with the dependency ( ) function . ''' )
2013-09-24 03:34:41 +08:00
self . link ( linktarget )
2017-08-30 00:36:40 +08:00
lwhole = extract_as_list ( kwargs , ' link_whole ' )
2017-04-03 00:06:16 +08:00
for linktarget in lwhole :
self . link_whole ( linktarget )
2017-08-29 17:20:25 +08:00
2019-01-29 12:14:19 +08:00
c_pchlist , cpp_pchlist , clist , cpplist , cudalist , cslist , valalist , objclist , objcpplist , fortranlist , rustlist \
2020-03-05 05:04:24 +08:00
= [ extract_as_list ( kwargs , c ) for c in [ ' c_pch ' , ' cpp_pch ' , ' c_args ' , ' cpp_args ' , ' cuda_args ' , ' cs_args ' , ' vala_args ' , ' objc_args ' , ' objcpp_args ' , ' fortran_args ' , ' rust_args ' ] ]
2017-08-29 17:20:25 +08:00
2013-09-24 03:34:41 +08:00
self . add_pch ( ' c ' , c_pchlist )
self . add_pch ( ' cpp ' , cpp_pchlist )
2019-01-29 12:14:19 +08:00
compiler_args = { ' c ' : clist , ' cpp ' : cpplist , ' cuda ' : cudalist , ' cs ' : cslist , ' vala ' : valalist , ' objc ' : objclist , ' objcpp ' : objcpplist ,
2017-08-30 00:36:40 +08:00
' fortran ' : fortranlist , ' rust ' : rustlist
2017-08-29 17:20:25 +08:00
}
2017-08-30 00:36:40 +08:00
for key , value in compiler_args . items ( ) :
self . add_compiler_args ( key , value )
2017-08-29 17:20:25 +08:00
2018-05-10 16:29:10 +08:00
if not isinstance ( self , Executable ) or ' export_dynamic ' in kwargs :
2016-10-25 05:05:35 +08:00
self . vala_header = kwargs . get ( ' vala_header ' , self . name + ' .h ' )
self . vala_vapi = kwargs . get ( ' vala_vapi ' , self . name + ' .vapi ' )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . vala_gir = kwargs . get ( ' vala_gir ' , None )
2017-09-12 20:29:23 +08:00
2016-08-21 07:40:29 +08:00
dlist = stringlistify ( kwargs . get ( ' d_args ' , [ ] ) )
2016-08-18 16:42:12 +08:00
self . add_compiler_args ( ' d ' , dlist )
2017-09-12 20:29:23 +08:00
dfeatures = dict ( )
2017-09-17 23:21:06 +08:00
dfeature_unittest = kwargs . get ( ' d_unittest ' , False )
2017-09-12 20:29:23 +08:00
if dfeature_unittest :
dfeatures [ ' unittest ' ] = dfeature_unittest
2018-08-18 20:11:35 +08:00
dfeature_versions = kwargs . get ( ' d_module_versions ' , [ ] )
2017-09-12 20:29:23 +08:00
if dfeature_versions :
dfeatures [ ' versions ' ] = dfeature_versions
2018-08-18 20:11:35 +08:00
dfeature_debug = kwargs . get ( ' d_debug ' , [ ] )
if dfeature_debug :
dfeatures [ ' debug ' ] = dfeature_debug
2018-02-25 21:49:58 +08:00
if ' d_import_dirs ' in kwargs :
2020-03-03 03:11:17 +08:00
dfeature_import_dirs = unholder ( extract_as_list ( kwargs , ' d_import_dirs ' ) )
2018-02-25 21:49:58 +08:00
for d in dfeature_import_dirs :
if not isinstance ( d , IncludeDirs ) :
raise InvalidArguments ( ' Arguments to d_import_dirs must be include_directories. ' )
2017-09-12 20:29:23 +08:00
dfeatures [ ' import_dirs ' ] = dfeature_import_dirs
if dfeatures :
2018-02-13 06:00:11 +08:00
self . d_features = dfeatures
2017-09-12 20:29:23 +08:00
2017-10-01 15:09:39 +08:00
self . link_args = extract_as_list ( kwargs , ' link_args ' )
2014-06-22 22:10:00 +08:00
for i in self . link_args :
2014-03-08 01:51:13 +08:00
if not isinstance ( i , str ) :
2014-06-22 22:10:00 +08:00
raise InvalidArguments ( ' Link_args arguments must be strings. ' )
2017-07-15 17:51:45 +08:00
for l in self . link_args :
if ' -Wl,-rpath ' in l or l . startswith ( ' -rpath ' ) :
mlog . warning ( ''' Please do not define rpath with a linker argument, use install_rpath or build_rpath properties instead.
This will become a hard error in a future Meson release . ''' )
2017-04-20 06:32:24 +08:00
self . process_link_depends ( kwargs . get ( ' link_depends ' , [ ] ) , environment )
2017-03-23 11:02:01 +08:00
# Target-specific include dirs must be added BEFORE include dirs from
# internal deps (added inside self.add_deps()) to override them.
2017-08-30 00:36:40 +08:00
inclist = extract_as_list ( kwargs , ' include_directories ' )
2017-01-21 14:35:38 +08:00
self . add_include_dirs ( inclist )
2017-03-23 11:02:01 +08:00
# Add dependencies (which also have include_directories)
2017-08-30 00:36:40 +08:00
deplist = extract_as_list ( kwargs , ' dependencies ' )
2017-03-23 11:02:01 +08:00
self . add_deps ( deplist )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
# If an item in this list is False, the output corresponding to
# the list index of that item will not be installed
self . install_dir = typeslistify ( kwargs . get ( ' install_dir ' , [ None ] ) ,
( str , bool ) )
2018-04-25 12:39:59 +08:00
self . install_mode = kwargs . get ( ' install_mode ' , None )
2014-03-12 04:30:05 +08:00
main_class = kwargs . get ( ' main_class ' , ' ' )
if not isinstance ( main_class , str ) :
raise InvalidArguments ( ' Main class must be a string ' )
self . main_class = main_class
2014-03-23 01:25:19 +08:00
if isinstance ( self , Executable ) :
2020-09-28 01:21:59 +08:00
# This kwarg is deprecated. The value of "none" means that the kwarg
# was not specified and win_subsystem should be used instead.
self . gui_app = None
if ' gui_app ' in kwargs :
if ' win_subsystem ' in kwargs :
raise InvalidArguments ( ' Can specify only gui_app or win_subsystem for a target, not both. ' )
self . gui_app = kwargs [ ' gui_app ' ]
if not isinstance ( self . gui_app , bool ) :
raise InvalidArguments ( ' Argument gui_app must be boolean. ' )
self . win_subsystem = self . validate_win_subsystem ( kwargs . get ( ' win_subsystem ' , ' console ' ) )
2014-03-23 01:25:19 +08:00
elif ' gui_app ' in kwargs :
raise InvalidArguments ( ' Argument gui_app can only be used on executables. ' )
2020-09-28 01:21:59 +08:00
elif ' win_subsystem ' in kwargs :
raise InvalidArguments ( ' Argument win_subsystem can only be used on executables. ' )
2017-08-30 00:36:40 +08:00
extra_files = extract_as_list ( kwargs , ' extra_files ' )
2014-04-26 03:13:15 +08:00
for i in extra_files :
2017-05-01 01:58:25 +08:00
assert ( isinstance ( i , File ) )
trial = os . path . join ( environment . get_source_dir ( ) , i . subdir , i . fname )
2014-04-26 03:13:15 +08:00
if not ( os . path . isfile ( trial ) ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' Tried to add non-existing extra file {} . ' . format ( i ) )
2014-04-26 03:13:15 +08:00
self . extra_files = extra_files
2021-01-13 03:20:13 +08:00
self . install_rpath : str = kwargs . get ( ' install_rpath ' , ' ' )
2014-07-12 01:53:50 +08:00
if not isinstance ( self . install_rpath , str ) :
raise InvalidArguments ( ' Install_rpath is not a string. ' )
2017-07-15 17:33:03 +08:00
self . build_rpath = kwargs . get ( ' build_rpath ' , ' ' )
if not isinstance ( self . build_rpath , str ) :
raise InvalidArguments ( ' Build_rpath is not a string. ' )
2017-08-30 00:36:40 +08:00
resources = extract_as_list ( kwargs , ' resources ' )
2014-07-30 20:49:01 +08:00
for r in resources :
if not isinstance ( r , str ) :
raise InvalidArguments ( ' Resource argument is not a string. ' )
trial = os . path . join ( environment . get_source_dir ( ) , self . subdir , r )
if not os . path . isfile ( trial ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' Tried to add non-existing resource {} . ' . format ( r ) )
2014-07-30 20:49:01 +08:00
self . resources = resources
2016-02-13 03:57:31 +08:00
if ' name_prefix ' in kwargs :
name_prefix = kwargs [ ' name_prefix ' ]
2016-02-21 23:12:09 +08:00
if isinstance ( name_prefix , list ) :
Don't use len() to test emptiness vs not emptiness
Meson has a common pattern of using 'if len(foo) == 0:' or
'if len(foo) != 0:', however, this is a common anti-pattern in python.
Instead tests for emptiness/non-emptiness should be done with a simple
'if foo:' or 'if not foo:'
Consider the following:
>>> import timeit
>>> timeit.timeit('if len([]) == 0: pass')
0.10730923599840025
>>> timeit.timeit('if not []: pass')
0.030033907998586074
>>> timeit.timeit('if len(['a', 'b', 'c', 'd']) == 0: pass')
0.1154778649979562
>>> timeit.timeit("if not ['a', 'b', 'c', 'd']: pass")
0.08259823200205574
>>> timeit.timeit('if len("") == 0: pass')
0.089759664999292
>>> timeit.timeit('if not "": pass')
0.02340641999762738
>>> timeit.timeit('if len("foo") == 0: pass')
0.08848102600313723
>>> timeit.timeit('if not "foo": pass')
0.04032287199879647
And for the one additional case of 'if len(foo.strip()) == 0', which can
be replaced with 'if not foo.isspace()'
>>> timeit.timeit('if len(" ".strip()) == 0: pass')
0.15294511600222904
>>> timeit.timeit('if " ".isspace(): pass')
0.09413968399894657
>>> timeit.timeit('if len(" abc".strip()) == 0: pass')
0.2023209120015963
>>> timeit.timeit('if " abc".isspace(): pass')
0.09571301700270851
In other words, it's always a win to not use len(), when you don't
actually want to check the length.
2017-05-02 06:11:01 +08:00
if name_prefix :
2020-04-11 23:44:04 +08:00
raise InvalidArguments ( ' name_prefix array must be empty to signify default. ' )
else :
if not isinstance ( name_prefix , str ) :
raise InvalidArguments ( ' name_prefix must be a string. ' )
self . prefix = name_prefix
self . name_prefix_set = True
2016-02-21 23:12:09 +08:00
if ' name_suffix ' in kwargs :
name_suffix = kwargs [ ' name_suffix ' ]
if isinstance ( name_suffix , list ) :
Don't use len() to test emptiness vs not emptiness
Meson has a common pattern of using 'if len(foo) == 0:' or
'if len(foo) != 0:', however, this is a common anti-pattern in python.
Instead tests for emptiness/non-emptiness should be done with a simple
'if foo:' or 'if not foo:'
Consider the following:
>>> import timeit
>>> timeit.timeit('if len([]) == 0: pass')
0.10730923599840025
>>> timeit.timeit('if not []: pass')
0.030033907998586074
>>> timeit.timeit('if len(['a', 'b', 'c', 'd']) == 0: pass')
0.1154778649979562
>>> timeit.timeit("if not ['a', 'b', 'c', 'd']: pass")
0.08259823200205574
>>> timeit.timeit('if len("") == 0: pass')
0.089759664999292
>>> timeit.timeit('if not "": pass')
0.02340641999762738
>>> timeit.timeit('if len("foo") == 0: pass')
0.08848102600313723
>>> timeit.timeit('if not "foo": pass')
0.04032287199879647
And for the one additional case of 'if len(foo.strip()) == 0', which can
be replaced with 'if not foo.isspace()'
>>> timeit.timeit('if len(" ".strip()) == 0: pass')
0.15294511600222904
>>> timeit.timeit('if " ".isspace(): pass')
0.09413968399894657
>>> timeit.timeit('if len(" abc".strip()) == 0: pass')
0.2023209120015963
>>> timeit.timeit('if " abc".isspace(): pass')
0.09571301700270851
In other words, it's always a win to not use len(), when you don't
actually want to check the length.
2017-05-02 06:11:01 +08:00
if name_suffix :
2020-04-11 23:44:04 +08:00
raise InvalidArguments ( ' name_suffix array must be empty to signify default. ' )
2016-02-22 06:09:06 +08:00
else :
if not isinstance ( name_suffix , str ) :
2016-10-10 21:52:41 +08:00
raise InvalidArguments ( ' name_suffix must be a string. ' )
2018-05-30 17:30:37 +08:00
if name_suffix == ' ' :
raise InvalidArguments ( ' name_suffix should not be an empty string. '
' If you want meson to use the default behaviour '
' for each platform pass `[]` (empty array) ' )
2016-02-22 06:09:06 +08:00
self . suffix = name_suffix
2016-10-12 14:42:27 +08:00
self . name_suffix_set = True
2016-10-05 18:28:45 +08:00
if isinstance ( self , StaticLibrary ) :
2016-10-07 22:26:04 +08:00
# You can't disable PIC on OS X. The compiler ignores -fno-PIC.
# PIC is always on for Windows (all code is position-independent
# since library loading is done differently)
2018-10-05 08:52:08 +08:00
m = self . environment . machines [ self . for_machine ]
if m . is_darwin ( ) or m . is_windows ( ) :
2016-10-07 22:26:04 +08:00
self . pic = True
else :
2020-10-08 21:30:24 +08:00
self . pic = self . _extract_pic_pie ( kwargs , ' pic ' , environment , ' b_staticpic ' )
if isinstance ( self , Executable ) or ( isinstance ( self , StaticLibrary ) and not self . pic ) :
2018-10-02 08:48:34 +08:00
# Executables must be PIE on Android
2018-10-05 08:52:08 +08:00
if self . environment . machines [ self . for_machine ] . is_android ( ) :
2018-10-02 08:48:34 +08:00
self . pie = True
else :
2020-10-08 21:30:24 +08:00
self . pie = self . _extract_pic_pie ( kwargs , ' pie ' , environment , ' b_pie ' )
2017-07-31 23:35:25 +08:00
self . implicit_include_directories = kwargs . get ( ' implicit_include_directories ' , True )
if not isinstance ( self . implicit_include_directories , bool ) :
raise InvalidArguments ( ' Implicit_include_directories must be a boolean. ' )
2018-08-10 00:46:45 +08:00
self . gnu_symbol_visibility = kwargs . get ( ' gnu_symbol_visibility ' , ' ' )
if not isinstance ( self . gnu_symbol_visibility , str ) :
raise InvalidArguments ( ' GNU symbol visibility must be a string. ' )
if self . gnu_symbol_visibility != ' ' :
2018-07-04 04:16:33 +08:00
permitted = [ ' default ' , ' internal ' , ' hidden ' , ' protected ' , ' inlineshidden ' ]
2018-08-10 00:46:45 +08:00
if self . gnu_symbol_visibility not in permitted :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' GNU symbol visibility arg {} not one of: {} ' . format ( self . symbol_visibility , ' , ' . join ( permitted ) ) )
2013-09-24 03:34:41 +08:00
2020-10-14 01:44:41 +08:00
def validate_win_subsystem ( self , value : str ) - > str :
2020-09-28 01:21:59 +08:00
value = value . lower ( )
if re . fullmatch ( r ' (boot_application|console|efi_application|efi_boot_service_driver|efi_rom|efi_runtime_driver|native|posix|windows)(, \ d+( \ . \ d+)?)? ' , value ) is None :
raise InvalidArguments ( ' Invalid value for win_subsystem: {} . ' . format ( value ) )
return value
2020-12-05 08:09:10 +08:00
def _extract_pic_pie ( self , kwargs , arg : str , environment , option : str ) :
2018-10-02 08:48:34 +08:00
# Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags
all_flags = self . extra_args [ ' c ' ] + self . extra_args [ ' cpp ' ]
if ' -f ' + arg . lower ( ) in all_flags or ' -f ' + arg . upper ( ) in all_flags :
mlog . warning ( " Use the ' {} ' kwarg instead of passing ' {} ' manually to {!r} " . format ( arg , ' -f ' + arg , self . name ) )
return True
2020-12-05 08:09:10 +08:00
k = OptionKey ( option )
2020-10-08 21:30:24 +08:00
if arg in kwargs :
val = kwargs [ arg ]
2020-12-05 09:01:45 +08:00
elif k in environment . coredata . options :
val = environment . coredata . options [ k ] . value
2020-10-08 21:30:24 +08:00
else :
val = False
2018-10-02 08:48:34 +08:00
if not isinstance ( val , bool ) :
raise InvalidArguments ( ' Argument {} to {!r} must be boolean ' . format ( arg , self . name ) )
return val
2013-09-24 03:34:41 +08:00
def get_filename ( self ) :
return self . filename
2020-09-01 20:28:08 +08:00
def get_outputs ( self ) - > T . List [ str ] :
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
return self . outputs
2016-10-15 06:42:26 +08:00
2013-09-24 03:34:41 +08:00
def get_extra_args ( self , language ) :
return self . extra_args . get ( language , [ ] )
2020-07-22 11:05:17 +08:00
def get_dependencies ( self , exclude = None ) :
2014-09-03 04:06:07 +08:00
transitive_deps = [ ]
2018-04-13 04:20:20 +08:00
if exclude is None :
exclude = [ ]
2019-09-19 11:50:12 +08:00
for t in itertools . chain ( self . link_targets , self . link_whole_targets ) :
2018-04-13 04:20:20 +08:00
if t in transitive_deps or t in exclude :
continue
2020-07-22 11:05:17 +08:00
transitive_deps . append ( t )
2014-09-03 04:06:07 +08:00
if isinstance ( t , StaticLibrary ) :
2020-07-22 11:05:17 +08:00
transitive_deps + = t . get_dependencies ( transitive_deps + exclude )
2014-09-03 04:06:07 +08:00
return transitive_deps
2013-09-24 03:34:41 +08:00
def get_source_subdir ( self ) :
return self . subdir
def get_sources ( self ) :
return self . sources
2013-11-05 06:16:17 +08:00
def get_objects ( self ) :
return self . objects
2013-09-24 03:34:41 +08:00
def get_generated_sources ( self ) :
return self . generated
2020-09-01 20:28:08 +08:00
def should_install ( self ) - > bool :
2013-09-24 03:34:41 +08:00
return self . need_install
def has_pch ( self ) :
return len ( self . pch ) > 0
def get_pch ( self , language ) :
try :
return self . pch [ language ]
except KeyError :
return [ ]
def get_include_dirs ( self ) :
return self . include_dirs
2016-10-14 20:17:52 +08:00
def add_deps ( self , deps ) :
2017-09-19 04:19:12 +08:00
deps = listify ( deps )
2020-03-06 01:50:30 +08:00
for dep in unholder ( deps ) :
2020-05-13 20:02:29 +08:00
if dep in self . added_deps :
continue
2015-06-15 05:26:19 +08:00
if isinstance ( dep , dependencies . InternalDependency ) :
2016-02-29 00:02:50 +08:00
# Those parts that are internal.
2015-07-01 01:58:58 +08:00
self . process_sourcelist ( dep . sources )
2019-09-30 03:27:05 +08:00
self . add_include_dirs ( dep . include_directories , dep . get_include_type ( ) )
2015-06-15 05:26:19 +08:00
for l in dep . libraries :
self . link ( l )
2018-02-18 11:16:29 +08:00
for l in dep . whole_libraries :
self . link_whole ( l )
2019-09-25 00:47:39 +08:00
if dep . get_compile_args ( ) or dep . get_link_args ( ) :
2018-04-12 22:33:49 +08:00
# Those parts that are external.
extpart = dependencies . InternalDependency ( ' undefined ' ,
[ ] ,
2019-09-25 00:47:39 +08:00
dep . get_compile_args ( ) ,
dep . get_link_args ( ) ,
2020-01-08 03:28:00 +08:00
[ ] , [ ] , [ ] , [ ] , { } )
2018-04-12 22:33:49 +08:00
self . external_deps . append ( extpart )
2016-02-29 00:02:50 +08:00
# Deps of deps.
2016-10-14 20:17:52 +08:00
self . add_deps ( dep . ext_deps )
2018-01-04 22:21:39 +08:00
elif isinstance ( dep , dependencies . Dependency ) :
2019-03-13 18:13:55 +08:00
if dep not in self . external_deps :
self . external_deps . append ( dep )
self . process_sourcelist ( dep . get_sources ( ) )
2019-04-06 07:19:30 +08:00
self . add_deps ( dep . ext_deps )
2017-01-06 00:35:08 +08:00
elif isinstance ( dep , BuildTarget ) :
raise InvalidArguments ( ''' Tried to use a build target as a dependency.
You probably should put it in link_with instead . ''' )
2015-06-15 05:26:19 +08:00
else :
2016-09-08 02:25:34 +08:00
# This is a bit of a hack. We do not want Build to know anything
# about the interpreter so we can't import it and use isinstance.
# This should be reliable enough.
2017-07-19 22:40:16 +08:00
if hasattr ( dep , ' project_args_frozen ' ) or hasattr ( dep , ' global_args_frozen ' ) :
2016-10-14 20:17:52 +08:00
raise InvalidArguments ( ' Tried to use subproject object as a dependency. \n '
2017-01-01 03:19:38 +08:00
' You probably wanted to use a dependency declared in it instead. \n '
' Access it by calling get_variable() on the subproject object. ' )
2016-10-14 20:17:52 +08:00
raise InvalidArguments ( ' Argument is of an unacceptable type {!r} . \n Must be '
2017-01-01 03:19:38 +08:00
' either an external dependency (returned by find_library() or '
' dependency()) or an internal dependency (returned by '
' declare_dependency()). ' . format ( type ( dep ) . __name__ ) )
2020-05-13 20:02:29 +08:00
self . added_deps . add ( dep )
2013-09-24 03:34:41 +08:00
def get_external_deps ( self ) :
return self . external_deps
2019-09-19 09:23:03 +08:00
def is_internal ( self ) :
return isinstance ( self , StaticLibrary ) and not self . need_install
2013-09-24 03:34:41 +08:00
def link ( self , target ) :
2020-03-03 03:11:17 +08:00
for t in unholder ( listify ( target ) ) :
2020-09-22 18:51:14 +08:00
if isinstance ( self , StaticLibrary ) and self . need_install :
if isinstance ( t , ( CustomTarget , CustomTargetIndex ) ) :
if not t . should_install ( ) :
mlog . warning ( ' Try to link an installed static library target {} with a custom target '
' that is not installed, this might cause problems when you try to use '
' this static library ' . format ( self . name ) )
elif t . is_internal ( ) :
# When we're a static library and we link_with to an
# internal/convenience library, promote to link_whole.
return self . link_whole ( t )
2019-04-29 22:57:02 +08:00
if not isinstance ( t , ( Target , CustomTargetIndex ) ) :
2017-10-24 03:37:06 +08:00
raise InvalidArguments ( ' {!r} is not a target. ' . format ( t ) )
2017-04-14 20:58:21 +08:00
if not t . is_linkable_target ( ) :
2020-02-13 02:47:51 +08:00
raise InvalidArguments ( " Link target ' {!s} ' is not linkable. " . format ( t ) )
2016-10-07 22:26:04 +08:00
if isinstance ( self , SharedLibrary ) and isinstance ( t , StaticLibrary ) and not t . pic :
2016-10-10 21:52:41 +08:00
msg = " Can ' t link non-PIC static library {!r} into shared library {!r} . " . format ( t . name , self . name )
msg + = " Use the ' pic ' option to static_library to build with PIC. "
raise InvalidArguments ( msg )
2018-10-05 08:52:08 +08:00
if self . for_machine is not t . for_machine :
2019-06-28 01:36:09 +08:00
msg = ' Tried to mix libraries for machines {} and {} in target {!r} ' . format ( self . for_machine , t . for_machine , self . name )
2018-10-05 08:52:08 +08:00
if self . environment . is_cross_build ( ) :
raise InvalidArguments ( msg + ' This is not possible in a cross build. ' )
else :
mlog . warning ( msg + ' This will fail in cross build. ' )
2014-09-21 00:01:48 +08:00
self . link_targets . append ( t )
2013-09-24 03:34:41 +08:00
2017-04-03 00:06:16 +08:00
def link_whole ( self , target ) :
2020-03-03 03:11:17 +08:00
for t in unholder ( listify ( target ) ) :
2019-04-29 22:57:02 +08:00
if isinstance ( t , ( CustomTarget , CustomTargetIndex ) ) :
2019-03-29 04:56:37 +08:00
if not t . is_linkable_target ( ) :
raise InvalidArguments ( ' Custom target {!r} is not linkable. ' . format ( t ) )
if not t . get_filename ( ) . endswith ( ' .a ' ) :
raise InvalidArguments ( ' Can only link_whole custom targets that are .a archives. ' )
2019-11-23 02:26:53 +08:00
if isinstance ( self , StaticLibrary ) :
# FIXME: We could extract the .a archive to get object files
raise InvalidArguments ( ' Cannot link_whole a custom target into a static library ' )
2019-03-29 04:56:37 +08:00
elif not isinstance ( t , StaticLibrary ) :
2017-04-03 00:06:16 +08:00
raise InvalidArguments ( ' {!r} is not a static library. ' . format ( t ) )
2019-11-23 02:26:53 +08:00
elif isinstance ( self , SharedLibrary ) and not t . pic :
2017-04-03 00:06:16 +08:00
msg = " Can ' t link non-PIC static library {!r} into shared library {!r} . " . format ( t . name , self . name )
msg + = " Use the ' pic ' option to static_library to build with PIC. "
raise InvalidArguments ( msg )
2018-10-05 08:52:08 +08:00
if self . for_machine is not t . for_machine :
2019-12-04 20:42:00 +08:00
msg = ' Tried to mix libraries for machines {1} and {2} in target {0!r} ' . format ( self . name , self . for_machine , t . for_machine )
2018-10-05 08:52:08 +08:00
if self . environment . is_cross_build ( ) :
raise InvalidArguments ( msg + ' This is not possible in a cross build. ' )
else :
mlog . warning ( msg + ' This will fail in cross build. ' )
2019-09-19 09:12:55 +08:00
if isinstance ( self , StaticLibrary ) :
# When we're a static library and we link_whole: to another static
# library, we need to add that target's objects to ourselves.
2019-09-29 05:24:46 +08:00
self . objects + = t . extract_all_objects_recurse ( )
2019-09-19 11:50:12 +08:00
self . link_whole_targets . append ( t )
2017-04-03 00:06:16 +08:00
2019-09-29 05:24:46 +08:00
def extract_all_objects_recurse ( self ) :
objs = [ self . extract_all_objects ( ) ]
for t in self . link_targets :
if t . is_internal ( ) :
objs + = t . extract_all_objects_recurse ( )
return objs
2013-09-24 03:34:41 +08:00
def add_pch ( self , language , pchlist ) :
Don't use len() to test emptiness vs not emptiness
Meson has a common pattern of using 'if len(foo) == 0:' or
'if len(foo) != 0:', however, this is a common anti-pattern in python.
Instead tests for emptiness/non-emptiness should be done with a simple
'if foo:' or 'if not foo:'
Consider the following:
>>> import timeit
>>> timeit.timeit('if len([]) == 0: pass')
0.10730923599840025
>>> timeit.timeit('if not []: pass')
0.030033907998586074
>>> timeit.timeit('if len(['a', 'b', 'c', 'd']) == 0: pass')
0.1154778649979562
>>> timeit.timeit("if not ['a', 'b', 'c', 'd']: pass")
0.08259823200205574
>>> timeit.timeit('if len("") == 0: pass')
0.089759664999292
>>> timeit.timeit('if not "": pass')
0.02340641999762738
>>> timeit.timeit('if len("foo") == 0: pass')
0.08848102600313723
>>> timeit.timeit('if not "foo": pass')
0.04032287199879647
And for the one additional case of 'if len(foo.strip()) == 0', which can
be replaced with 'if not foo.isspace()'
>>> timeit.timeit('if len(" ".strip()) == 0: pass')
0.15294511600222904
>>> timeit.timeit('if " ".isspace(): pass')
0.09413968399894657
>>> timeit.timeit('if len(" abc".strip()) == 0: pass')
0.2023209120015963
>>> timeit.timeit('if " abc".isspace(): pass')
0.09571301700270851
In other words, it's always a win to not use len(), when you don't
actually want to check the length.
2017-05-02 06:11:01 +08:00
if not pchlist :
2013-09-24 03:34:41 +08:00
return
2014-02-24 04:07:54 +08:00
elif len ( pchlist ) == 1 :
if not environment . is_header ( pchlist [ 0 ] ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' PCH argument {} is not a header. ' . format ( pchlist [ 0 ] ) )
2014-02-24 04:07:54 +08:00
elif len ( pchlist ) == 2 :
2013-09-24 03:34:41 +08:00
if environment . is_header ( pchlist [ 0 ] ) :
if not environment . is_source ( pchlist [ 1 ] ) :
2013-10-17 03:33:33 +08:00
raise InvalidArguments ( ' PCH definition must contain one header and at most one source. ' )
2013-09-24 03:34:41 +08:00
elif environment . is_source ( pchlist [ 0 ] ) :
if not environment . is_header ( pchlist [ 1 ] ) :
2013-10-17 03:33:33 +08:00
raise InvalidArguments ( ' PCH definition must contain one header and at most one source. ' )
2013-09-24 03:34:41 +08:00
pchlist = [ pchlist [ 1 ] , pchlist [ 0 ] ]
else :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' PCH argument {} is of unknown type. ' . format ( pchlist [ 0 ] ) )
2019-01-28 05:20:44 +08:00
if ( os . path . dirname ( pchlist [ 0 ] ) != os . path . dirname ( pchlist [ 1 ] ) ) :
raise InvalidArguments ( ' PCH files must be stored in the same folder. ' )
2019-01-27 23:52:21 +08:00
mlog . warning ( ' PCH source files are deprecated, only a single header file should be used. ' )
2013-09-24 03:34:41 +08:00
elif len ( pchlist ) > 2 :
2013-10-17 03:33:33 +08:00
raise InvalidArguments ( ' PCH definition may have a maximum of 2 files. ' )
2019-01-01 20:05:26 +08:00
for f in pchlist :
2019-03-22 03:01:07 +08:00
if not isinstance ( f , str ) :
raise MesonException ( ' PCH arguments must be strings. ' )
2019-01-01 20:05:26 +08:00
if not os . path . isfile ( os . path . join ( self . environment . source_dir , self . subdir , f ) ) :
2020-03-04 03:45:43 +08:00
raise MesonException ( ' File {} does not exist. ' . format ( f ) )
2013-09-24 03:34:41 +08:00
self . pch [ language ] = pchlist
2020-01-06 22:27:38 +08:00
def add_include_dirs ( self , args , set_is_system : T . Optional [ str ] = None ) :
2013-09-24 04:08:50 +08:00
ids = [ ]
2020-03-06 01:50:30 +08:00
for a in unholder ( args ) :
2013-09-24 03:34:41 +08:00
if not isinstance ( a , IncludeDirs ) :
raise InvalidArguments ( ' Include directory to be added is not an include directory object. ' )
2013-09-24 04:08:50 +08:00
ids . append ( a )
2019-09-30 03:27:05 +08:00
if set_is_system is None :
set_is_system = ' preserve '
if set_is_system != ' preserve ' :
is_system = set_is_system == ' system '
ids = [ IncludeDirs ( x . get_curdir ( ) , x . get_incdirs ( ) , is_system , x . get_extra_build_dirs ( ) ) for x in ids ]
2013-09-24 04:08:50 +08:00
self . include_dirs + = ids
2013-09-24 03:34:41 +08:00
2020-10-22 07:00:17 +08:00
def add_compiler_args ( self , language : str , args : T . List [ ' FileOrString ' ] ) - > None :
2017-10-01 15:09:39 +08:00
args = listify ( args )
2014-06-22 22:10:00 +08:00
for a in args :
2015-07-04 03:18:09 +08:00
if not isinstance ( a , ( str , File ) ) :
2013-09-24 03:34:41 +08:00
raise InvalidArguments ( ' A non-string passed to compiler args. ' )
if language in self . extra_args :
2014-06-22 22:10:00 +08:00
self . extra_args [ language ] + = args
2013-09-24 03:34:41 +08:00
else :
2014-06-22 22:10:00 +08:00
self . extra_args [ language ] = args
2013-09-24 03:34:41 +08:00
2021-01-13 03:20:13 +08:00
def get_aliases ( self ) - > T . Dict [ str , str ] :
2016-12-22 04:41:36 +08:00
return { }
2013-09-24 03:34:41 +08:00
2020-01-06 22:27:38 +08:00
def get_langs_used_by_deps ( self ) - > T . List [ str ] :
2017-04-21 18:37:03 +08:00
'''
Sometimes you want to link to a C + + library that exports C API , which
means the linker must link in the C + + stdlib , and we must use a C + +
compiler for linking . The same is also applicable for objc / objc + + , etc ,
2018-06-19 12:17:25 +08:00
so we can keep using clink_langs for the priority order .
2017-04-21 18:37:03 +08:00
See : https : / / github . com / mesonbuild / meson / issues / 1653
'''
2020-03-25 02:09:13 +08:00
langs = [ ] # type: T.List[str]
2019-05-03 04:26:51 +08:00
2017-04-21 18:37:03 +08:00
# Check if any of the external libraries were written in this language
for dep in self . external_deps :
2018-04-26 03:25:55 +08:00
if dep . language is None :
continue
2017-04-21 18:37:03 +08:00
if dep . language not in langs :
langs . append ( dep . language )
# Check if any of the internal libraries this target links to were
# written in this language
2017-07-25 04:44:43 +08:00
for link_target in itertools . chain ( self . link_targets , self . link_whole_targets ) :
2019-04-29 22:57:02 +08:00
if isinstance ( link_target , ( CustomTarget , CustomTargetIndex ) ) :
2019-03-18 04:34:19 +08:00
continue
2017-04-21 18:37:03 +08:00
for language in link_target . compilers :
if language not in langs :
langs . append ( language )
2019-05-03 04:26:51 +08:00
2017-04-21 18:37:03 +08:00
return langs
2020-11-14 22:43:00 +08:00
def get_prelinker ( self ) :
all_compilers = self . environment . coredata . compilers [ self . for_machine ]
if self . link_language :
comp = all_compilers [ self . link_language ]
return comp
for l in clink_langs :
if l in self . compilers :
try :
prelinker = all_compilers [ l ]
except KeyError :
raise MesonException (
' Could not get a prelinker linker for build target {!r} . '
' Requires a compiler for language " {} " , but that is not '
' a project language. ' . format ( self . name , l ) )
return prelinker
raise MesonException ( ' Could not determine prelinker for {!r} . ' . format ( self . name ) )
2018-06-19 12:17:25 +08:00
def get_clink_dynamic_linker_and_stdlibs ( self ) :
2016-12-09 15:23:10 +08:00
'''
2018-06-19 12:17:25 +08:00
We use the order of languages in ` clink_langs ` to determine which
2016-12-09 15:23:10 +08:00
linker to use in case the target has sources compiled with multiple
compilers . All languages other than those in this list have their own
linker .
Note that Vala outputs C code , so Vala sources can use any linker
that can link compiled C . We don ' t actually need to add an exception
for Vala here because of that .
'''
2017-04-21 18:37:03 +08:00
# Populate list of all compilers, not just those being used to compile
# sources in this target
2018-10-05 08:52:08 +08:00
all_compilers = self . environment . coredata . compilers [ self . for_machine ]
2020-03-25 03:02:14 +08:00
# If the user set the link_language, just return that.
if self . link_language :
comp = all_compilers [ self . link_language ]
return comp , comp . language_stdlib_only_link_flags ( )
2017-04-21 18:37:03 +08:00
# Languages used by dependencies
dep_langs = self . get_langs_used_by_deps ( )
# Pick a compiler based on the language priority-order
2018-06-19 12:17:25 +08:00
for l in clink_langs :
2017-04-21 18:37:03 +08:00
if l in self . compilers or l in dep_langs :
2017-04-25 07:56:36 +08:00
try :
2018-04-26 03:25:55 +08:00
linker = all_compilers [ l ]
2017-04-25 07:56:36 +08:00
except KeyError :
raise MesonException (
' Could not get a dynamic linker for build target {!r} . '
' Requires a linker for language " {} " , but that is not '
' a project language. ' . format ( self . name , l ) )
2018-04-26 03:25:55 +08:00
stdlib_args = [ ]
added_languages = set ( )
for dl in itertools . chain ( self . compilers , dep_langs ) :
if dl != linker . language :
stdlib_args + = all_compilers [ dl ] . language_stdlib_only_link_flags ( )
added_languages . add ( dl )
2020-03-03 03:11:17 +08:00
# Type of var 'linker' is Compiler.
2020-02-16 19:39:02 +08:00
# Pretty hard to fix because the return value is passed everywhere
2018-04-26 03:25:55 +08:00
return linker , stdlib_args
2017-04-25 07:56:36 +08:00
2017-04-21 18:37:03 +08:00
m = ' Could not get a dynamic linker for build target {!r} '
raise AssertionError ( m . format ( self . name ) )
2016-12-09 15:23:10 +08:00
2021-01-20 02:12:38 +08:00
def uses_rust ( self ) - > bool :
2020-10-22 07:00:17 +08:00
""" Is this target a rust target. """
2021-01-06 07:55:02 +08:00
if self . sources :
first_file = self . sources [ 0 ]
if first_file . fname . endswith ( ' .rs ' ) :
return True
elif self . generated :
if self . generated [ 0 ] . get_outputs ( ) [ 0 ] . endswith ( ' .rs ' ) :
return True
return False
2019-03-23 05:40:52 +08:00
2016-12-09 15:25:20 +08:00
def get_using_msvc ( self ) :
'''
Check if the dynamic linker is MSVC . Used by Executable , StaticLibrary ,
and SharedLibrary for deciding when to use MSVC - specific file naming
and debug filenames .
If at least some code is built with MSVC and the final library is
linked with MSVC , we can be sure that some debug info will be
generated . We only check the dynamic linker here because the static
linker is guaranteed to be of the same type .
Interesting cases :
1. The Vala compiler outputs C code to be compiled by whatever
C compiler we ' re using, so all objects will still be created by the
MSVC compiler .
2. If the target contains only objects , process_compilers guesses and
picks the first compiler that smells right .
'''
2021-01-06 07:55:02 +08:00
# Rustc can use msvc style linkers
if self . uses_rust ( ) :
compiler = self . environment . coredata . compilers [ self . for_machine ] [ ' rust ' ]
else :
compiler , _ = self . get_clink_dynamic_linker_and_stdlibs ( )
2018-04-26 03:25:55 +08:00
# Mixing many languages with MSVC is not supported yet so ignore stdlibs.
2021-01-06 07:55:02 +08:00
return compiler and compiler . get_linker_id ( ) in { ' link ' , ' lld-link ' , ' xilink ' , ' optlink ' }
2016-12-09 15:25:20 +08:00
2018-03-21 16:49:36 +08:00
def check_module_linking ( self ) :
'''
Warn if shared modules are linked with target : ( link_with ) #2865
'''
for link_target in self . link_targets :
if isinstance ( link_target , SharedModule ) :
2018-10-05 08:52:08 +08:00
if self . environment . machines [ self . for_machine ] . is_darwin ( ) :
2020-10-14 02:02:41 +08:00
raise MesonException (
' target links against shared modules. This is not permitted on OSX ' )
2018-06-03 01:12:10 +08:00
else :
2020-10-14 02:02:41 +08:00
mlog . warning ( ' target links against shared modules. This '
' is not recommended as it is not supported on some '
' platforms ' )
2018-03-24 00:48:27 +08:00
return
2013-09-24 04:08:50 +08:00
2017-01-17 21:13:03 +08:00
class Generator :
2013-09-24 04:08:50 +08:00
def __init__ ( self , args , kwargs ) :
if len ( args ) != 1 :
2016-10-14 20:17:52 +08:00
raise InvalidArguments ( ' Generator requires exactly one positional argument: the executable ' )
2020-03-06 01:50:30 +08:00
exe = unholder ( args [ 0 ] )
2016-08-27 20:47:29 +08:00
if not isinstance ( exe , ( Executable , dependencies . ExternalProgram ) ) :
2015-03-10 01:39:12 +08:00
raise InvalidArguments ( ' First generator argument must be an executable. ' )
2013-09-24 04:08:50 +08:00
self . exe = exe
2016-08-21 04:11:16 +08:00
self . depfile = None
2017-09-21 02:28:04 +08:00
self . capture = False
2019-05-20 05:59:20 +08:00
self . depends = [ ]
2013-09-24 04:08:50 +08:00
self . process_kwargs ( kwargs )
2016-07-01 17:13:51 +08:00
def __repr__ ( self ) :
repr_str = " < {0} : {1} > "
return repr_str . format ( self . __class__ . __name__ , self . exe )
2013-09-24 04:08:50 +08:00
def get_exe ( self ) :
return self . exe
def process_kwargs ( self , kwargs ) :
if ' arguments ' not in kwargs :
raise InvalidArguments ( ' Generator must have " arguments " keyword argument. ' )
args = kwargs [ ' arguments ' ]
if isinstance ( args , str ) :
args = [ args ]
if not isinstance ( args , list ) :
raise InvalidArguments ( ' " Arguments " keyword argument must be a string or a list of strings. ' )
for a in args :
if not isinstance ( a , str ) :
raise InvalidArguments ( ' A non-string object in " arguments " keyword argument. ' )
self . arglist = args
2014-05-24 23:26:34 +08:00
if ' output ' not in kwargs :
raise InvalidArguments ( ' Generator must have " output " keyword argument. ' )
2017-09-19 04:19:12 +08:00
outputs = listify ( kwargs [ ' output ' ] )
2013-09-24 04:08:50 +08:00
for rule in outputs :
if not isinstance ( rule , str ) :
2014-05-24 23:26:34 +08:00
raise InvalidArguments ( ' " output " may only contain strings. ' )
2016-12-20 04:48:35 +08:00
if ' @BASENAME@ ' not in rule and ' @PLAINNAME@ ' not in rule :
2015-10-04 04:18:40 +08:00
raise InvalidArguments ( ' Every element of " output " must contain @BASENAME@ or @PLAINNAME@. ' )
2017-05-29 22:59:28 +08:00
if has_path_sep ( rule ) :
2013-09-24 04:08:50 +08:00
raise InvalidArguments ( ' " outputs " must not contain a directory separator. ' )
2014-03-22 22:46:38 +08:00
if len ( outputs ) > 1 :
for o in outputs :
if ' @OUTPUT@ ' in o :
raise InvalidArguments ( ' Tried to use @OUTPUT@ in a rule with more than one output. ' )
2013-09-24 04:08:50 +08:00
self . outputs = outputs
2016-08-21 04:11:16 +08:00
if ' depfile ' in kwargs :
depfile = kwargs [ ' depfile ' ]
if not isinstance ( depfile , str ) :
raise InvalidArguments ( ' Depfile must be a string. ' )
2018-01-29 08:10:43 +08:00
if os . path . basename ( depfile ) != depfile :
2016-08-21 04:11:16 +08:00
raise InvalidArguments ( ' Depfile must be a plain filename without a subdirectory. ' )
self . depfile = depfile
2017-09-21 02:28:04 +08:00
if ' capture ' in kwargs :
capture = kwargs [ ' capture ' ]
if not isinstance ( capture , bool ) :
raise InvalidArguments ( ' Capture must be boolean. ' )
self . capture = capture
2019-05-20 05:59:20 +08:00
if ' depends ' in kwargs :
2020-03-03 03:11:17 +08:00
depends = unholder ( listify ( kwargs [ ' depends ' ] ) )
2019-05-20 05:59:20 +08:00
for d in depends :
2020-03-16 04:25:09 +08:00
if not ( isinstance ( d , ( BuildTarget , CustomTarget ) ) ) :
2019-05-20 05:59:20 +08:00
raise InvalidArguments ( ' Depends entries must be build targets. ' )
self . depends . append ( d )
2013-09-24 04:08:50 +08:00
def get_base_outnames ( self , inname ) :
2018-01-29 08:10:43 +08:00
plainname = os . path . basename ( inname )
2016-10-13 08:58:33 +08:00
basename = os . path . splitext ( plainname ) [ 0 ]
2017-12-12 04:14:10 +08:00
bases = [ x . replace ( ' @BASENAME@ ' , basename ) . replace ( ' @PLAINNAME@ ' , plainname ) for x in self . outputs ]
return bases
2013-09-24 04:08:50 +08:00
2016-08-21 04:11:16 +08:00
def get_dep_outname ( self , inname ) :
if self . depfile is None :
raise InvalidArguments ( ' Tried to get dep name for rule that does not have dependency file defined. ' )
2018-01-29 08:10:43 +08:00
plainname = os . path . basename ( inname )
2016-10-13 08:58:33 +08:00
basename = os . path . splitext ( plainname ) [ 0 ]
2016-08-21 04:11:16 +08:00
return self . depfile . replace ( ' @BASENAME@ ' , basename ) . replace ( ' @PLAINNAME@ ' , plainname )
2017-09-19 00:09:41 +08:00
def get_arglist ( self , inname ) :
2018-01-29 08:10:43 +08:00
plainname = os . path . basename ( inname )
2017-09-19 00:09:41 +08:00
basename = os . path . splitext ( plainname ) [ 0 ]
return [ x . replace ( ' @BASENAME@ ' , basename ) . replace ( ' @PLAINNAME@ ' , plainname ) for x in self . arglist ]
2013-09-24 04:08:50 +08:00
2017-12-12 04:14:10 +08:00
def is_parent_path ( self , parent , trial ) :
2017-12-19 02:08:00 +08:00
relpath = pathlib . PurePath ( trial ) . relative_to ( parent )
return relpath . parts [ 0 ] != ' .. ' # For subdirs we can only go "down".
2017-12-12 04:14:10 +08:00
2019-04-23 05:26:18 +08:00
def process_files ( self , name , files , state , preserve_path_from = None , extra_args = None ) :
output = GeneratedList ( self , state . subdir , preserve_path_from , extra_args = extra_args if extra_args is not None else [ ] )
2016-12-20 16:56:46 +08:00
for f in files :
2016-12-29 05:20:18 +08:00
if isinstance ( f , str ) :
f = File . from_source_file ( state . environment . source_dir , state . subdir , f )
elif not isinstance ( f , File ) :
raise InvalidArguments ( ' {} arguments must be strings or files not {!r} . ' . format ( name , f ) )
2017-12-18 04:42:20 +08:00
if preserve_path_from :
2017-12-12 04:14:10 +08:00
abs_f = f . absolute_path ( state . environment . source_dir , state . environment . build_dir )
2017-12-18 04:42:20 +08:00
if not self . is_parent_path ( preserve_path_from , abs_f ) :
2017-12-12 04:14:10 +08:00
raise InvalidArguments ( ' When using preserve_path_from, all input files must be in a subdirectory of the given dir. ' )
output . add_file ( f , state )
2016-12-20 16:56:46 +08:00
return output
2017-01-17 21:13:03 +08:00
class GeneratedList :
2019-04-23 05:26:18 +08:00
def __init__ ( self , generator , subdir , preserve_path_from = None , extra_args = None ) :
2020-03-06 01:50:30 +08:00
self . generator = unholder ( generator )
2016-10-15 07:57:35 +08:00
self . name = self . generator . exe
2017-12-19 02:08:00 +08:00
self . subdir = subdir
2013-09-24 04:08:50 +08:00
self . infilelist = [ ]
self . outfilelist = [ ]
self . outmap = { }
2015-04-09 04:05:14 +08:00
self . extra_depends = [ ]
2019-03-19 18:49:11 +08:00
self . depend_files = [ ]
2017-12-18 04:42:20 +08:00
self . preserve_path_from = preserve_path_from
2019-04-23 05:26:18 +08:00
self . extra_args = extra_args if extra_args is not None else [ ]
2020-03-06 01:50:30 +08:00
if isinstance ( self . generator . exe , dependencies . ExternalProgram ) :
if not self . generator . exe . found ( ) :
2019-03-19 18:49:11 +08:00
raise InvalidArguments ( ' Tried to use not-found external program as generator ' )
2020-03-06 01:50:30 +08:00
path = self . generator . exe . get_path ( )
2019-03-19 18:49:11 +08:00
if os . path . isabs ( path ) :
# Can only add a dependency on an external program which we
# know the absolute path of
self . depend_files . append ( File . from_absolute_file ( path ) )
2013-09-24 04:08:50 +08:00
2017-12-12 04:14:10 +08:00
def add_preserved_path_segment ( self , infile , outfiles , state ) :
result = [ ]
in_abs = infile . absolute_path ( state . environment . source_dir , state . environment . build_dir )
2017-12-18 04:42:20 +08:00
assert ( os . path . isabs ( self . preserve_path_from ) )
rel = os . path . relpath ( in_abs , self . preserve_path_from )
2018-01-29 08:10:43 +08:00
path_segment = os . path . dirname ( rel )
2017-12-12 04:14:10 +08:00
for of in outfiles :
result . append ( os . path . join ( path_segment , of ) )
return result
def add_file ( self , newfile , state ) :
2013-09-24 04:08:50 +08:00
self . infilelist . append ( newfile )
2016-12-29 05:20:18 +08:00
outfiles = self . generator . get_base_outnames ( newfile . fname )
2017-12-18 04:42:20 +08:00
if self . preserve_path_from :
2017-12-12 04:14:10 +08:00
outfiles = self . add_preserved_path_segment ( newfile , outfiles , state )
2013-09-24 04:08:50 +08:00
self . outfilelist + = outfiles
self . outmap [ newfile ] = outfiles
2016-10-15 06:42:26 +08:00
def get_inputs ( self ) :
2013-09-24 04:08:50 +08:00
return self . infilelist
2020-09-01 20:28:08 +08:00
def get_outputs ( self ) - > T . List [ str ] :
2013-09-24 04:08:50 +08:00
return self . outfilelist
def get_outputs_for ( self , filename ) :
return self . outmap [ filename ]
def get_generator ( self ) :
return self . generator
2015-11-29 01:47:52 +08:00
def get_extra_args ( self ) :
return self . extra_args
2013-09-24 03:34:41 +08:00
class Executable ( BuildTarget ) :
2018-03-30 01:36:46 +08:00
known_kwargs = known_exe_kwargs
2020-10-22 07:00:17 +08:00
def __init__ ( self , name : str , subdir : str , subproject : str , for_machine : MachineChoice ,
sources : T . List [ File ] , objects , environment : environment . Environment , kwargs ) :
2018-12-30 18:31:03 +08:00
self . typename = ' executable '
2020-12-05 08:09:10 +08:00
key = OptionKey ( ' b_pie ' )
2020-12-05 09:01:45 +08:00
if ' pie ' not in kwargs and key in environment . coredata . options :
kwargs [ ' pie ' ] = environment . coredata . options [ key ] . value
2018-10-05 08:52:08 +08:00
super ( ) . __init__ ( name , subdir , subproject , for_machine , sources , objects , environment , kwargs )
2017-11-26 13:55:40 +08:00
# Unless overridden, executables have no suffix or prefix. Except on
2016-07-01 17:13:51 +08:00
# Windows and with C#/Mono executables where the suffix is 'exe'
if not hasattr ( self , ' prefix ' ) :
self . prefix = ' '
if not hasattr ( self , ' suffix ' ) :
2018-10-05 08:52:08 +08:00
machine = environment . machines [ for_machine ]
2016-07-01 17:13:51 +08:00
# Executable for Windows or C#/Mono
2018-10-05 08:52:08 +08:00
if machine . is_windows ( ) or machine . is_cygwin ( ) or ' cs ' in self . compilers :
2016-07-01 17:13:51 +08:00
self . suffix = ' exe '
2019-06-20 02:56:00 +08:00
elif machine . system . startswith ( ' wasm ' ) or machine . system == ' emscripten ' :
self . suffix = ' js '
2018-10-25 10:24:05 +08:00
elif ( ' c ' in self . compilers and self . compilers [ ' c ' ] . get_id ( ) . startswith ( ' arm ' ) or
' cpp ' in self . compilers and self . compilers [ ' cpp ' ] . get_id ( ) . startswith ( ' arm ' ) ) :
self . suffix = ' axf '
elif ( ' c ' in self . compilers and self . compilers [ ' c ' ] . get_id ( ) . startswith ( ' ccrx ' ) or
' cpp ' in self . compilers and self . compilers [ ' cpp ' ] . get_id ( ) . startswith ( ' ccrx ' ) ) :
self . suffix = ' abs '
2020-03-21 04:13:42 +08:00
elif ( ' c ' in self . compilers and self . compilers [ ' c ' ] . get_id ( ) . startswith ( ' xc16 ' ) ) :
self . suffix = ' elf '
elif ( ' c ' in self . compilers and self . compilers [ ' c ' ] . get_id ( ) . startswith ( ' c2000 ' ) or
' cpp ' in self . compilers and self . compilers [ ' cpp ' ] . get_id ( ) . startswith ( ' c2000 ' ) ) :
self . suffix = ' out '
2016-07-01 17:13:51 +08:00
else :
2018-10-05 08:52:08 +08:00
self . suffix = environment . machines [ for_machine ] . get_exe_suffix ( )
2016-07-01 17:13:51 +08:00
self . filename = self . name
if self . suffix :
self . filename + = ' . ' + self . suffix
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . outputs = [ self . filename ]
2013-09-24 03:34:41 +08:00
2017-04-14 20:58:21 +08:00
# The import library this target will generate
self . import_filename = None
# The import library that Visual Studio would generate (and accept)
self . vs_import_filename = None
# The import library that GCC would generate (and prefer)
self . gcc_import_filename = None
2019-03-06 22:45:14 +08:00
# The debugging information file this target will generate
self . debug_filename = None
2017-04-14 20:58:21 +08:00
2018-01-10 03:36:13 +08:00
# Check for export_dynamic
self . export_dynamic = False
if kwargs . get ( ' export_dynamic ' ) :
if not isinstance ( kwargs [ ' export_dynamic ' ] , bool ) :
raise InvalidArguments ( ' " export_dynamic " keyword argument must be a boolean ' )
self . export_dynamic = True
if kwargs . get ( ' implib ' ) :
self . export_dynamic = True
if self . export_dynamic and kwargs . get ( ' implib ' ) is False :
raise InvalidArguments ( ' " implib " keyword argument must not be false for if " export_dynamic " is true ' )
2019-03-06 22:45:14 +08:00
m = environment . machines [ for_machine ]
2018-01-10 03:36:13 +08:00
# If using export_dynamic, set the import library name
if self . export_dynamic :
2017-06-23 03:18:15 +08:00
implib_basename = self . name + ' .exe '
2018-01-10 03:36:13 +08:00
if not isinstance ( kwargs . get ( ' implib ' , False ) , bool ) :
2017-06-23 03:18:15 +08:00
implib_basename = kwargs [ ' implib ' ]
2018-10-05 08:52:08 +08:00
if m . is_windows ( ) or m . is_cygwin ( ) :
2017-06-23 03:18:15 +08:00
self . vs_import_filename = ' {0} .lib ' . format ( implib_basename )
self . gcc_import_filename = ' lib {0} .a ' . format ( implib_basename )
2017-04-14 20:58:21 +08:00
if self . get_using_msvc ( ) :
self . import_filename = self . vs_import_filename
else :
self . import_filename = self . gcc_import_filename
2019-03-06 22:45:14 +08:00
if m . is_windows ( ) and ( ' cs ' in self . compilers or
2021-01-20 02:12:38 +08:00
self . uses_rust ( ) or
2019-03-06 22:45:14 +08:00
self . get_using_msvc ( ) ) :
self . debug_filename = self . name + ' .pdb '
2018-01-10 03:36:13 +08:00
# Only linkwithable if using export_dynamic
self . is_linkwithable = self . export_dynamic
2021-01-13 03:20:13 +08:00
def get_default_install_dir ( self , environment : environment . Environment ) - > str :
2018-09-30 02:54:42 +08:00
return environment . get_bindir ( )
2018-08-19 23:19:11 +08:00
def description ( self ) :
''' Human friendly description of the executable '''
return self . name
2015-07-05 06:47:34 +08:00
def type_suffix ( self ) :
return " @exe "
2013-09-24 03:34:41 +08:00
2017-04-14 20:58:21 +08:00
def get_import_filename ( self ) :
"""
The name of the import library that will be outputted by the compiler
Returns None if there is no import library required for this platform
"""
return self . import_filename
def get_import_filenameslist ( self ) :
if self . import_filename :
return [ self . vs_import_filename , self . gcc_import_filename ]
return [ ]
2019-03-06 22:45:14 +08:00
def get_debug_filename ( self ) :
"""
The name of debuginfo file that will be created by the compiler
Returns None if the build won ' t create any debuginfo file
"""
return self . debug_filename
2017-04-14 20:58:21 +08:00
def is_linkable_target ( self ) :
return self . is_linkwithable
2013-09-24 03:34:41 +08:00
class StaticLibrary ( BuildTarget ) :
2018-03-30 01:36:46 +08:00
known_kwargs = known_stlib_kwargs
2018-10-05 08:52:08 +08:00
def __init__ ( self , name , subdir , subproject , for_machine : MachineChoice , sources , objects , environment , kwargs ) :
2018-12-30 18:31:03 +08:00
self . typename = ' static library '
2018-10-05 08:52:08 +08:00
super ( ) . __init__ ( name , subdir , subproject , for_machine , sources , objects , environment , kwargs )
2016-09-25 13:46:57 +08:00
if ' cs ' in self . compilers :
2014-07-19 04:12:14 +08:00
raise InvalidArguments ( ' Static libraries not supported for C#. ' )
2017-06-10 05:51:58 +08:00
if ' rust ' in self . compilers :
# If no crate type is specified, or it's the generic lib type, use rlib
2017-06-14 23:39:41 +08:00
if not hasattr ( self , ' rust_crate_type ' ) or self . rust_crate_type == ' lib ' :
2017-06-10 05:51:58 +08:00
mlog . debug ( ' Defaulting Rust static library target crate type to rlib ' )
2017-06-14 23:39:41 +08:00
self . rust_crate_type = ' rlib '
2017-06-10 05:51:58 +08:00
# Don't let configuration proceed with a non-static crate type
2017-06-14 23:39:41 +08:00
elif self . rust_crate_type not in [ ' rlib ' , ' staticlib ' ] :
raise InvalidArguments ( ' Crate type " {0} " invalid for static libraries; must be " rlib " or " staticlib " ' . format ( self . rust_crate_type ) )
2016-07-01 17:13:51 +08:00
# By default a static library is named libfoo.a even on Windows because
# MSVC does not have a consistent convention for what static libraries
# are called. The MSVC CRT uses libfoo.lib syntax but nothing else uses
# it and GCC only looks for static libraries called foo.lib and
# libfoo.a. However, we cannot use foo.lib because that's the same as
# the import library. Using libfoo.a is ok because people using MSVC
# always pass the library filename while linking anyway.
2016-02-13 03:57:31 +08:00
if not hasattr ( self , ' prefix ' ) :
2016-07-01 17:13:51 +08:00
self . prefix = ' lib '
if not hasattr ( self , ' suffix ' ) :
2016-09-25 13:46:57 +08:00
if ' rust ' in self . compilers :
2017-06-14 23:39:41 +08:00
if not hasattr ( self , ' rust_crate_type ' ) or self . rust_crate_type == ' rlib ' :
2017-06-10 05:51:58 +08:00
# default Rust static library suffix
self . suffix = ' rlib '
2017-06-14 23:39:41 +08:00
elif self . rust_crate_type == ' staticlib ' :
2017-06-10 05:51:58 +08:00
self . suffix = ' a '
2016-07-01 17:13:51 +08:00
else :
self . suffix = ' a '
2014-04-01 03:07:43 +08:00
self . filename = self . prefix + self . name + ' . ' + self . suffix
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . outputs = [ self . filename ]
2020-11-14 22:43:00 +08:00
self . prelink = kwargs . get ( ' prelink ' , False )
if not isinstance ( self . prelink , bool ) :
raise InvalidArguments ( ' Prelink keyword argument must be a boolean. ' )
2013-09-24 03:34:41 +08:00
2018-09-30 03:15:00 +08:00
def get_link_deps_mapping ( self , prefix , environment ) :
return { }
2018-09-30 02:54:42 +08:00
def get_default_install_dir ( self , environment ) :
return environment . get_static_lib_dir ( )
2015-07-05 06:47:34 +08:00
def type_suffix ( self ) :
return " @sta "
2017-06-10 05:51:58 +08:00
def process_kwargs ( self , kwargs , environment ) :
super ( ) . process_kwargs ( kwargs , environment )
2017-06-14 23:39:41 +08:00
if ' rust_crate_type ' in kwargs :
rust_crate_type = kwargs [ ' rust_crate_type ' ]
if isinstance ( rust_crate_type , str ) :
self . rust_crate_type = rust_crate_type
2017-06-10 05:51:58 +08:00
else :
2017-06-14 23:39:41 +08:00
raise InvalidArguments ( ' Invalid rust_crate_type " {0} " : must be a string. ' . format ( rust_crate_type ) )
2017-06-10 05:51:58 +08:00
2017-04-14 20:58:21 +08:00
def is_linkable_target ( self ) :
return True
2013-09-24 03:34:41 +08:00
class SharedLibrary ( BuildTarget ) :
2018-03-30 01:36:46 +08:00
known_kwargs = known_shlib_kwargs
2018-10-05 08:52:08 +08:00
def __init__ ( self , name , subdir , subproject , for_machine : MachineChoice , sources , objects , environment , kwargs ) :
2018-12-30 18:31:03 +08:00
self . typename = ' shared library '
2013-09-24 03:34:41 +08:00
self . soversion = None
2016-07-01 17:13:51 +08:00
self . ltversion = None
2018-08-28 17:47:00 +08:00
# Max length 2, first element is compatibility_version, second is current_version
self . darwin_versions = [ ]
2016-02-15 11:35:10 +08:00
self . vs_module_defs = None
2016-07-01 17:13:51 +08:00
# The import library this target will generate
self . import_filename = None
# The import library that Visual Studio would generate (and accept)
self . vs_import_filename = None
# The import library that GCC would generate (and prefer)
self . gcc_import_filename = None
2019-03-06 22:45:14 +08:00
# The debugging information file this target will generate
self . debug_filename = None
2018-10-05 08:52:08 +08:00
super ( ) . __init__ ( name , subdir , subproject , for_machine , sources , objects , environment , kwargs )
2017-06-10 05:51:58 +08:00
if ' rust ' in self . compilers :
# If no crate type is specified, or it's the generic lib type, use dylib
2017-06-14 23:39:41 +08:00
if not hasattr ( self , ' rust_crate_type ' ) or self . rust_crate_type == ' lib ' :
2017-06-10 05:51:58 +08:00
mlog . debug ( ' Defaulting Rust dynamic library target crate type to " dylib " ' )
2017-06-14 23:39:41 +08:00
self . rust_crate_type = ' dylib '
2017-06-10 05:51:58 +08:00
# Don't let configuration proceed with a non-dynamic crate type
2017-06-14 23:39:41 +08:00
elif self . rust_crate_type not in [ ' dylib ' , ' cdylib ' ] :
raise InvalidArguments ( ' Crate type " {0} " invalid for dynamic libraries; must be " dylib " or " cdylib " ' . format ( self . rust_crate_type ) )
2016-07-01 17:13:51 +08:00
if not hasattr ( self , ' prefix ' ) :
self . prefix = None
if not hasattr ( self , ' suffix ' ) :
self . suffix = None
self . basic_filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2018-10-05 08:52:08 +08:00
self . determine_filenames ( environment )
2016-07-01 17:13:51 +08:00
2021-01-13 03:20:13 +08:00
def get_link_deps_mapping ( self , prefix , environment ) - > T . Dict [ str , str ] :
result : T . Dict [ str , str ] = { }
2018-09-30 03:15:00 +08:00
mappings = self . get_transitive_link_deps_mapping ( prefix , environment )
old = get_target_macos_dylib_install_name ( self )
if old not in mappings :
fname = self . get_filename ( )
outdirs , _ = self . get_install_dir ( self . environment )
new = os . path . join ( prefix , outdirs [ 0 ] , fname )
result . update ( { old : new } )
mappings . update ( result )
return mappings
2018-09-30 02:54:42 +08:00
def get_default_install_dir ( self , environment ) :
return environment . get_shared_lib_dir ( )
2018-10-05 08:52:08 +08:00
def determine_filenames ( self , env ) :
2016-07-01 17:13:51 +08:00
"""
See https : / / github . com / mesonbuild / meson / pull / 417 for details .
First we determine the filename template ( self . filename_tpl ) , then we
set the output filename ( self . filename ) .
2016-12-22 04:41:36 +08:00
The template is needed while creating aliases ( self . get_aliases ) ,
2016-07-01 17:13:51 +08:00
which are needed while generating . so shared libraries for Linux .
Besides this , there ' s also the import library name, which is only used
on Windows since on that platform the linker uses a separate library
called the " import library " during linking instead of the shared
library ( DLL ) . The toolchain will output an import library in one of
two formats : GCC or Visual Studio .
When we ' re building with Visual Studio, the import library that will be
generated by the toolchain is self . vs_import_filename , and with
MinGW / GCC , it ' s self.gcc_import_filename. self.import_filename will
always contain the import library name this target will generate .
"""
prefix = ' '
suffix = ' '
2019-03-06 22:45:14 +08:00
create_debug_file = False
2016-07-01 17:13:51 +08:00
self . filename_tpl = self . basic_filename_tpl
# NOTE: manual prefix/suffix override is currently only tested for C/C++
# C# and Mono
2018-11-02 22:20:22 +08:00
if ' cs ' in self . compilers :
2016-07-01 17:13:51 +08:00
prefix = ' '
suffix = ' dll '
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2019-03-06 22:45:14 +08:00
create_debug_file = True
2016-07-01 17:13:51 +08:00
# C, C++, Swift, Vala
# Only Windows uses a separate import library for linking
# For all other targets/platforms import_filename stays None
2018-10-05 08:52:08 +08:00
elif env . machines [ self . for_machine ] . is_windows ( ) :
2016-02-21 23:12:09 +08:00
suffix = ' dll '
2018-10-31 06:03:53 +08:00
self . vs_import_filename = ' {0} {1} .lib ' . format ( self . prefix if self . prefix is not None else ' ' , self . name )
self . gcc_import_filename = ' {0} {1} .dll.a ' . format ( self . prefix if self . prefix is not None else ' lib ' , self . name )
2021-01-20 02:12:38 +08:00
if self . uses_rust ( ) :
2019-03-23 05:40:52 +08:00
# Shared library is of the form foo.dll
prefix = ' '
# Import library is called foo.dll.lib
self . import_filename = ' {0} .dll.lib ' . format ( self . name )
2019-03-06 22:45:14 +08:00
create_debug_file = True
2019-03-23 05:40:52 +08:00
elif self . get_using_msvc ( ) :
2016-07-01 17:13:51 +08:00
# Shared library is of the form foo.dll
prefix = ' '
# Import library is called foo.lib
self . import_filename = self . vs_import_filename
2019-03-06 22:45:14 +08:00
create_debug_file = True
2016-07-01 17:13:51 +08:00
# Assume GCC-compatible naming
else :
# Shared library is of the form libfoo.dll
prefix = ' lib '
# Import library is called libfoo.dll.a
self . import_filename = self . gcc_import_filename
# Shared library has the soversion if it is defined
if self . soversion :
self . filename_tpl = ' {0.prefix} {0.name} - {0.soversion} . {0.suffix} '
else :
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2018-10-05 08:52:08 +08:00
elif env . machines [ self . for_machine ] . is_cygwin ( ) :
2017-04-04 19:35:24 +08:00
suffix = ' dll '
2018-10-31 06:03:53 +08:00
self . gcc_import_filename = ' {0} {1} .dll.a ' . format ( self . prefix if self . prefix is not None else ' lib ' , self . name )
2017-04-04 19:35:24 +08:00
# Shared library is of the form cygfoo.dll
# (ld --dll-search-prefix=cyg is the default)
prefix = ' cyg '
# Import library is called libfoo.dll.a
self . import_filename = self . gcc_import_filename
if self . soversion :
self . filename_tpl = ' {0.prefix} {0.name} - {0.soversion} . {0.suffix} '
else :
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2018-10-05 08:52:08 +08:00
elif env . machines [ self . for_machine ] . is_darwin ( ) :
2016-07-01 17:13:51 +08:00
prefix = ' lib '
suffix = ' dylib '
2017-03-04 15:39:05 +08:00
# On macOS, the filename can only contain the major version
if self . soversion :
# libfoo.X.dylib
self . filename_tpl = ' {0.prefix} {0.name} . {0.soversion} . {0.suffix} '
else :
# libfoo.dylib
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2018-10-05 08:52:08 +08:00
elif env . machines [ self . for_machine ] . is_android ( ) :
2018-01-16 00:09:38 +08:00
prefix = ' lib '
suffix = ' so '
# Android doesn't support shared_library versioning
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2014-07-19 04:12:14 +08:00
else :
2016-07-01 17:13:51 +08:00
prefix = ' lib '
suffix = ' so '
if self . ltversion :
# libfoo.so.X[.Y[.Z]] (.Y and .Z are optional)
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} . {0.ltversion} '
elif self . soversion :
# libfoo.so.X
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} . {0.soversion} '
2016-02-21 23:12:09 +08:00
else :
2016-07-01 17:13:51 +08:00
# No versioning, libfoo.so
self . filename_tpl = ' {0.prefix} {0.name} . {0.suffix} '
2017-01-06 20:34:19 +08:00
if self . prefix is None :
2016-07-01 17:13:51 +08:00
self . prefix = prefix
2017-01-06 20:34:19 +08:00
if self . suffix is None :
2016-07-01 17:13:51 +08:00
self . suffix = suffix
self . filename = self . filename_tpl . format ( self )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . outputs = [ self . filename ]
2019-03-06 22:45:14 +08:00
if create_debug_file :
self . debug_filename = os . path . splitext ( self . filename ) [ 0 ] + ' .pdb '
2013-09-24 03:34:41 +08:00
2018-08-28 17:47:00 +08:00
@staticmethod
def _validate_darwin_versions ( darwin_versions ) :
try :
if isinstance ( darwin_versions , int ) :
darwin_versions = str ( darwin_versions )
if isinstance ( darwin_versions , str ) :
darwin_versions = 2 * [ darwin_versions ]
if not isinstance ( darwin_versions , list ) :
raise InvalidArguments ( ' Shared library darwin_versions: must be a string, integer, '
' or a list, not {!r} ' . format ( darwin_versions ) )
if len ( darwin_versions ) > 2 :
raise InvalidArguments ( ' Shared library darwin_versions: list must contain 2 or fewer elements ' )
if len ( darwin_versions ) == 1 :
darwin_versions = 2 * darwin_versions
for i , v in enumerate ( darwin_versions [ : ] ) :
if isinstance ( v , int ) :
v = str ( v )
if not isinstance ( v , str ) :
raise InvalidArguments ( ' Shared library darwin_versions: list elements '
' must be strings or integers, not {!r} ' . format ( v ) )
if not re . fullmatch ( r ' [0-9]+( \ .[0-9]+) { 0,2} ' , v ) :
raise InvalidArguments ( ' Shared library darwin_versions: must be X.Y.Z where '
' X, Y, Z are numbers, and Y and Z are optional ' )
parts = v . split ( ' . ' )
if len ( parts ) in ( 1 , 2 , 3 ) and int ( parts [ 0 ] ) > 65535 :
raise InvalidArguments ( ' Shared library darwin_versions: must be X.Y.Z '
' where X is [0, 65535] and Y, Z are optional ' )
if len ( parts ) in ( 2 , 3 ) and int ( parts [ 1 ] ) > 255 :
raise InvalidArguments ( ' Shared library darwin_versions: must be X.Y.Z '
' where Y is [0, 255] and Y, Z are optional ' )
if len ( parts ) == 3 and int ( parts [ 2 ] ) > 255 :
raise InvalidArguments ( ' Shared library darwin_versions: must be X.Y.Z '
' where Z is [0, 255] and Y, Z are optional ' )
darwin_versions [ i ] = v
except ValueError :
raise InvalidArguments ( ' Shared library darwin_versions: value is invalid ' )
return darwin_versions
2015-06-20 18:45:25 +08:00
def process_kwargs ( self , kwargs , environment ) :
super ( ) . process_kwargs ( kwargs , environment )
2018-01-16 00:09:38 +08:00
2018-10-05 08:52:08 +08:00
if not self . environment . machines [ self . for_machine ] . is_android ( ) :
2018-01-16 00:09:38 +08:00
supports_versioning = True
else :
supports_versioning = False
if supports_versioning :
# Shared library version
if ' version ' in kwargs :
self . ltversion = kwargs [ ' version ' ]
if not isinstance ( self . ltversion , str ) :
raise InvalidArguments ( ' Shared library version needs to be a string, not ' + type ( self . ltversion ) . __name__ )
if not re . fullmatch ( r ' [0-9]+( \ .[0-9]+) { 0,2} ' , self . ltversion ) :
raise InvalidArguments ( ' Invalid Shared library version " {0} " . Must be of the form X.Y.Z where all three are numbers. Y and Z are optional. ' . format ( self . ltversion ) )
# Try to extract/deduce the soversion
if ' soversion ' in kwargs :
self . soversion = kwargs [ ' soversion ' ]
if isinstance ( self . soversion , int ) :
self . soversion = str ( self . soversion )
if not isinstance ( self . soversion , str ) :
raise InvalidArguments ( ' Shared library soversion is not a string or integer. ' )
elif self . ltversion :
# library version is defined, get the soversion from that
# We replicate what Autotools does here and take the first
# number of the version by default.
self . soversion = self . ltversion . split ( ' . ' ) [ 0 ]
2019-06-12 19:08:29 +08:00
# macOS, iOS and tvOS dylib compatibility_version and current_version
2018-08-28 17:47:00 +08:00
if ' darwin_versions ' in kwargs :
self . darwin_versions = self . _validate_darwin_versions ( kwargs [ ' darwin_versions ' ] )
elif self . soversion :
# If unspecified, pick the soversion
self . darwin_versions = 2 * [ self . soversion ]
2016-07-01 17:13:51 +08:00
# Visual Studio module-definitions file
2016-02-15 11:35:10 +08:00
if ' vs_module_defs ' in kwargs :
2020-03-06 01:50:30 +08:00
path = unholder ( kwargs [ ' vs_module_defs ' ] )
2017-04-11 06:02:56 +08:00
if isinstance ( path , str ) :
if os . path . isabs ( path ) :
self . vs_module_defs = File . from_absolute_file ( path )
else :
self . vs_module_defs = File . from_source_file ( environment . source_dir , self . subdir , path )
2017-04-20 06:32:24 +08:00
self . link_depends . append ( self . vs_module_defs )
2017-04-11 06:02:56 +08:00
elif isinstance ( path , File ) :
# When passing a generated file.
self . vs_module_defs = path
2017-04-20 06:32:24 +08:00
self . link_depends . append ( path )
2017-05-08 06:20:50 +08:00
elif hasattr ( path , ' get_filename ' ) :
# When passing output of a Custom Target
path = File . from_built_file ( path . subdir , path . get_filename ( ) )
self . vs_module_defs = path
self . link_depends . append ( path )
2017-04-11 06:40:38 +08:00
else :
raise InvalidArguments (
' Shared library vs_module_defs must be either a string, '
2017-05-08 06:20:50 +08:00
' a file object or a Custom Target ' )
2017-06-14 23:39:41 +08:00
if ' rust_crate_type ' in kwargs :
rust_crate_type = kwargs [ ' rust_crate_type ' ]
if isinstance ( rust_crate_type , str ) :
self . rust_crate_type = rust_crate_type
2017-06-10 05:51:58 +08:00
else :
2017-06-14 23:39:41 +08:00
raise InvalidArguments ( ' Invalid rust_crate_type " {0} " : must be a string. ' . format ( rust_crate_type ) )
2015-06-20 18:45:25 +08:00
2014-03-20 04:26:47 +08:00
def get_import_filename ( self ) :
2016-07-01 17:13:51 +08:00
"""
The name of the import library that will be outputted by the compiler
2014-03-20 04:26:47 +08:00
2016-07-01 17:13:51 +08:00
Returns None if there is no import library required for this platform
"""
return self . import_filename
2014-04-03 04:40:04 +08:00
2019-03-06 22:45:14 +08:00
def get_debug_filename ( self ) :
"""
The name of debuginfo file that will be created by the compiler
Returns None if the build won ' t create any debuginfo file
"""
return self . debug_filename
2016-07-01 17:13:51 +08:00
def get_import_filenameslist ( self ) :
if self . import_filename :
return [ self . vs_import_filename , self . gcc_import_filename ]
return [ ]
2013-09-24 03:34:41 +08:00
2016-07-01 17:13:51 +08:00
def get_all_link_deps ( self ) :
return [ self ] + self . get_transitive_link_deps ( )
2013-09-24 03:34:41 +08:00
2021-01-13 03:20:13 +08:00
def get_aliases ( self ) - > T . Dict [ str , str ] :
2016-07-01 17:13:51 +08:00
"""
If the versioned library name is libfoo . so .0 .100 .0 , aliases are :
2016-12-22 04:41:36 +08:00
* libfoo . so .0 ( soversion ) - > libfoo . so .0 .100 .0
* libfoo . so ( unversioned ; for linking ) - > libfoo . so .0
2017-03-04 15:39:05 +08:00
Same for dylib :
* libfoo . dylib ( unversioned ; for linking ) - > libfoo .0 . dylib
2016-07-01 17:13:51 +08:00
"""
2021-01-13 03:20:13 +08:00
aliases : T . Dict [ str , str ] = { }
2017-03-04 15:39:05 +08:00
# Aliases are only useful with .so and .dylib libraries. Also if
# there's no self.soversion (no versioning), we don't need aliases.
if self . suffix not in ( ' so ' , ' dylib ' ) or not self . soversion :
2021-01-13 03:20:13 +08:00
return aliases
2017-03-04 15:39:05 +08:00
# With .so libraries, the minor and micro versions are also in the
# filename. If ltversion != soversion we create an soversion alias:
2016-12-22 04:41:36 +08:00
# libfoo.so.0 -> libfoo.so.0.100.0
2017-03-04 15:39:05 +08:00
# Where libfoo.so.0.100.0 is the actual library
if self . suffix == ' so ' and self . ltversion and self . ltversion != self . soversion :
2016-07-01 17:13:51 +08:00
alias_tpl = self . filename_tpl . replace ( ' ltversion ' , ' soversion ' )
2016-12-22 04:41:36 +08:00
ltversion_filename = alias_tpl . format ( self )
aliases [ ltversion_filename ] = self . filename
2017-03-04 15:39:05 +08:00
# libfoo.so.0/libfoo.0.dylib is the actual library
2016-12-22 04:41:36 +08:00
else :
ltversion_filename = self . filename
2017-03-04 15:39:05 +08:00
# Unversioned alias:
# libfoo.so -> libfoo.so.0
# libfoo.dylib -> libfoo.0.dylib
2016-12-22 04:41:36 +08:00
aliases [ self . basic_filename_tpl . format ( self ) ] = ltversion_filename
2013-09-24 03:34:41 +08:00
return aliases
2013-09-28 23:54:09 +08:00
2015-07-05 06:47:34 +08:00
def type_suffix ( self ) :
return " @sha "
2017-04-14 20:58:21 +08:00
def is_linkable_target ( self ) :
return True
2016-12-03 03:55:56 +08:00
# A shared library that is meant to be used with dlopen rather than linking
# into something else.
class SharedModule ( SharedLibrary ) :
2018-03-30 01:36:46 +08:00
known_kwargs = known_shmod_kwargs
2018-10-05 08:52:08 +08:00
def __init__ ( self , name , subdir , subproject , for_machine : MachineChoice , sources , objects , environment , kwargs ) :
2016-12-03 03:55:56 +08:00
if ' version ' in kwargs :
raise MesonException ( ' Shared modules must not specify the version kwarg. ' )
if ' soversion ' in kwargs :
raise MesonException ( ' Shared modules must not specify the soversion kwarg. ' )
2018-10-05 08:52:08 +08:00
super ( ) . __init__ ( name , subdir , subproject , for_machine , sources , objects , environment , kwargs )
2018-11-29 02:40:16 +08:00
self . typename = ' shared module '
2016-12-03 03:55:56 +08:00
2018-09-30 02:54:42 +08:00
def get_default_install_dir ( self , environment ) :
return environment . get_shared_module_dir ( )
2017-01-14 22:46:37 +08:00
class CustomTarget ( Target ) :
2018-03-30 01:36:46 +08:00
known_kwargs = set ( [
' input ' ,
' output ' ,
' command ' ,
' capture ' ,
' install ' ,
' install_dir ' ,
2018-04-25 12:39:59 +08:00
' install_mode ' ,
2018-03-30 01:36:46 +08:00
' build_always ' ,
2018-03-10 01:42:44 +08:00
' build_always_stale ' ,
2018-03-30 01:36:46 +08:00
' depends ' ,
' depend_files ' ,
' depfile ' ,
' build_by_default ' ,
' override_options ' ,
2017-11-12 23:27:53 +08:00
' console ' ,
2018-03-30 01:36:46 +08:00
] )
2015-04-02 21:43:35 +08:00
2019-08-18 02:12:56 +08:00
def __init__ ( self , name , subdir , subproject , kwargs , absolute_paths = False , backend = None ) :
2018-11-29 02:40:16 +08:00
self . typename = ' custom '
2018-10-05 08:52:08 +08:00
# TODO expose keyword arg to make MachineChoice.HOST configurable
super ( ) . __init__ ( name , subdir , subproject , False , MachineChoice . HOST )
2014-05-22 04:47:23 +08:00
self . dependencies = [ ]
2015-04-02 21:43:35 +08:00
self . extra_depends = [ ]
2015-10-27 03:12:22 +08:00
self . depend_files = [ ] # Files that this target depends on but are not on the command line.
2016-08-21 02:01:49 +08:00
self . depfile = None
2019-08-18 02:12:56 +08:00
self . process_kwargs ( kwargs , backend )
2016-12-15 16:05:46 +08:00
# Whether to use absolute paths for all files on the commandline
self . absolute_paths = absolute_paths
2014-08-13 03:51:36 +08:00
unknowns = [ ]
for k in kwargs :
if k not in CustomTarget . known_kwargs :
unknowns . append ( k )
if len ( unknowns ) > 0 :
2020-03-04 03:45:43 +08:00
mlog . warning ( ' Unknown keyword arguments in target {} : {} ' . format ( self . name , ' , ' . join ( unknowns ) ) )
2014-05-19 05:59:35 +08:00
2018-09-30 02:54:42 +08:00
def get_default_install_dir ( self , environment ) :
return None
2016-07-01 17:13:51 +08:00
def __repr__ ( self ) :
repr_str = " < {0} {1} : {2} > "
return repr_str . format ( self . __class__ . __name__ , self . get_id ( ) , self . command )
2016-03-16 07:00:39 +08:00
def get_target_dependencies ( self ) :
deps = self . dependencies [ : ]
deps + = self . extra_depends
2020-03-06 01:50:30 +08:00
for c in unholder ( self . sources ) :
2017-03-20 06:28:05 +08:00
if isinstance ( c , ( BuildTarget , CustomTarget ) ) :
2016-03-16 07:00:39 +08:00
deps . append ( c )
return deps
2017-11-28 05:34:39 +08:00
def get_transitive_build_target_deps ( self ) :
'''
Recursively fetch the build targets that this custom target depends on ,
whether through ` command : ` , ` depends : ` , or ` sources : ` The recursion is
only performed on custom targets .
This is useful for setting PATH on Windows for finding required DLLs .
F . ex , if you have a python script that loads a C module that links to
other DLLs in your project .
'''
bdeps = set ( )
deps = self . get_target_dependencies ( )
for d in deps :
if isinstance ( d , BuildTarget ) :
bdeps . add ( d )
elif isinstance ( d , CustomTarget ) :
bdeps . update ( d . get_transitive_build_target_deps ( ) )
return bdeps
2017-02-15 23:56:31 +08:00
def flatten_command ( self , cmd ) :
2020-03-03 03:11:17 +08:00
cmd = unholder ( listify ( cmd ) )
2017-02-15 23:56:31 +08:00
final_cmd = [ ]
for c in cmd :
2017-04-06 04:10:00 +08:00
if isinstance ( c , str ) :
final_cmd . append ( c )
elif isinstance ( c , File ) :
self . depend_files . append ( c )
2017-02-15 23:56:31 +08:00
final_cmd . append ( c )
elif isinstance ( c , dependencies . ExternalProgram ) :
if not c . found ( ) :
2019-03-19 18:49:11 +08:00
raise InvalidArguments ( ' Tried to use not-found external program in " command " ' )
2018-06-30 19:56:46 +08:00
path = c . get_path ( )
if os . path . isabs ( path ) :
# Can only add a dependency on an external program which we
# know the absolute path of
self . depend_files . append ( File . from_absolute_file ( path ) )
2017-02-15 23:56:31 +08:00
final_cmd + = c . get_command ( )
elif isinstance ( c , ( BuildTarget , CustomTarget ) ) :
self . dependencies . append ( c )
final_cmd . append ( c )
elif isinstance ( c , list ) :
final_cmd + = self . flatten_command ( c )
else :
raise InvalidArguments ( ' Argument {!r} in " command " is invalid ' . format ( c ) )
return final_cmd
2019-08-18 02:12:56 +08:00
def process_kwargs ( self , kwargs , backend ) :
2019-12-04 22:46:03 +08:00
self . process_kwargs_base ( kwargs )
2020-03-03 03:11:17 +08:00
self . sources = unholder ( extract_as_list ( kwargs , ' input ' ) )
2014-05-19 05:59:35 +08:00
if ' output ' not in kwargs :
raise InvalidArguments ( ' Missing keyword argument " output " . ' )
2017-09-19 04:19:12 +08:00
self . outputs = listify ( kwargs [ ' output ' ] )
2017-02-13 23:42:59 +08:00
# This will substitute values from the input into output and return it.
2019-08-18 02:12:56 +08:00
inputs = get_sources_string_names ( self . sources , backend )
2017-02-13 23:42:59 +08:00
values = get_filenames_templates_dict ( inputs , [ ] )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
for i in self . outputs :
2014-07-27 07:54:23 +08:00
if not ( isinstance ( i , str ) ) :
raise InvalidArguments ( ' Output argument not a string. ' )
2018-01-31 03:30:07 +08:00
if i == ' ' :
raise InvalidArguments ( ' Output must not be empty. ' )
if i . strip ( ) == ' ' :
raise InvalidArguments ( ' Output must not consist only of whitespace. ' )
2017-05-29 22:59:28 +08:00
if has_path_sep ( i ) :
2018-07-11 19:18:43 +08:00
raise InvalidArguments ( ' Output {!r} must not contain a path segment. ' . format ( i ) )
2017-02-13 23:42:59 +08:00
if ' @INPUT@ ' in i or ' @INPUT0@ ' in i :
m = ' Output cannot contain @INPUT@ or @INPUT0@, did you ' \
' mean @PLAINNAME@ or @BASENAME@? '
raise InvalidArguments ( m )
# We already check this during substitution, but the error message
# will be unclear/confusing, so check it here.
if len ( inputs ) != 1 and ( ' @PLAINNAME@ ' in i or ' @BASENAME@ ' in i ) :
m = " Output cannot contain @PLAINNAME@ or @BASENAME@ when " \
" there is more than one input (we can ' t know which to use) "
raise InvalidArguments ( m )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . outputs = substitute_values ( self . outputs , values )
2016-08-24 16:35:15 +08:00
self . capture = kwargs . get ( ' capture ' , False )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
if self . capture and len ( self . outputs ) != 1 :
2016-10-14 20:17:52 +08:00
raise InvalidArguments ( ' Capturing can only output to a single file. ' )
2017-11-12 23:27:53 +08:00
self . console = kwargs . get ( ' console ' , False )
if not isinstance ( self . console , bool ) :
raise InvalidArguments ( ' " console " kwarg only accepts booleans ' )
if self . capture and self . console :
raise InvalidArguments ( " Can ' t both capture output and output to console " )
2014-05-19 05:59:35 +08:00
if ' command ' not in kwargs :
raise InvalidArguments ( ' Missing keyword argument " command " . ' )
2016-08-21 02:01:49 +08:00
if ' depfile ' in kwargs :
depfile = kwargs [ ' depfile ' ]
if not isinstance ( depfile , str ) :
raise InvalidArguments ( ' Depfile must be a string. ' )
2018-01-29 08:10:43 +08:00
if os . path . basename ( depfile ) != depfile :
2016-08-21 02:01:49 +08:00
raise InvalidArguments ( ' Depfile must be a plain filename without a subdirectory. ' )
self . depfile = depfile
2017-02-15 23:56:31 +08:00
self . command = self . flatten_command ( kwargs [ ' command ' ] )
2016-08-27 19:41:14 +08:00
if self . capture :
for c in self . command :
if isinstance ( c , str ) and ' @OUTPUT@ ' in c :
raise InvalidArguments ( ' @OUTPUT@ is not allowed when capturing output. ' )
2014-05-19 05:59:35 +08:00
if ' install ' in kwargs :
self . install = kwargs [ ' install ' ]
if not isinstance ( self . install , bool ) :
raise InvalidArguments ( ' " install " must be boolean. ' )
2016-04-18 03:52:10 +08:00
if self . install :
if ' install_dir ' not in kwargs :
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
raise InvalidArguments ( ' " install_dir " must be specified '
' when installing a target ' )
2018-04-27 22:23:55 +08:00
if isinstance ( kwargs [ ' install_dir ' ] , list ) :
2020-05-13 01:53:37 +08:00
FeatureNew . single_use ( ' multiple install_dir for custom_target ' , ' 0.40.0 ' , self . subproject )
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
# If an item in this list is False, the output corresponding to
# the list index of that item will not be installed
self . install_dir = typeslistify ( kwargs [ ' install_dir ' ] , ( str , bool ) )
2018-04-25 12:39:59 +08:00
self . install_mode = kwargs . get ( ' install_mode ' , None )
2014-05-19 05:59:35 +08:00
else :
self . install = False
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . install_dir = [ None ]
2018-04-25 12:39:59 +08:00
self . install_mode = None
2018-03-10 01:42:44 +08:00
if ' build_always ' in kwargs and ' build_always_stale ' in kwargs :
raise InvalidArguments ( ' build_always and build_always_stale are mutually exclusive. Combine build_by_default and build_always_stale. ' )
elif ' build_always ' in kwargs :
2018-07-02 00:55:38 +08:00
if ' build_by_default ' not in kwargs :
self . build_by_default = kwargs [ ' build_always ' ]
2018-03-10 01:42:44 +08:00
self . build_always_stale = kwargs [ ' build_always ' ]
elif ' build_always_stale ' in kwargs :
self . build_always_stale = kwargs [ ' build_always_stale ' ]
if not isinstance ( self . build_always_stale , bool ) :
raise InvalidArguments ( ' Argument build_always_stale must be a boolean. ' )
2020-03-05 05:04:24 +08:00
extra_deps , depend_files = [ extract_as_list ( kwargs , c , pop = False ) for c in [ ' depends ' , ' depend_files ' ] ]
2020-03-06 01:50:30 +08:00
for ed in unholder ( extra_deps ) :
2016-08-27 20:47:29 +08:00
if not isinstance ( ed , ( CustomTarget , BuildTarget ) ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' Can only depend on toplevel targets: custom_target or build_target (executable or a library) got: {} ( {} ) '
. format ( type ( ed ) , ed ) )
2015-04-02 21:43:35 +08:00
self . extra_depends . append ( ed )
2015-10-27 03:12:22 +08:00
for i in depend_files :
if isinstance ( i , ( File , str ) ) :
self . depend_files . append ( i )
else :
mlog . debug ( i )
2016-10-14 20:17:52 +08:00
raise InvalidArguments ( ' Unknown type {!r} in depend_files. ' . format ( type ( i ) . __name__ ) )
2014-05-19 05:59:35 +08:00
def get_dependencies ( self ) :
2014-05-22 04:47:23 +08:00
return self . dependencies
2014-05-19 05:59:35 +08:00
2020-09-01 20:28:08 +08:00
def should_install ( self ) - > bool :
2014-05-19 05:59:35 +08:00
return self . install
def get_custom_install_dir ( self ) :
return self . install_dir
2014-05-22 04:47:23 +08:00
2018-04-25 12:39:59 +08:00
def get_custom_install_mode ( self ) :
return self . install_mode
2020-09-01 20:28:08 +08:00
def get_outputs ( self ) - > T . List [ str ] :
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
return self . outputs
2014-05-19 05:59:35 +08:00
2016-10-16 22:35:05 +08:00
def get_filename ( self ) :
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
return self . outputs [ 0 ]
2016-10-16 22:35:05 +08:00
2014-05-23 21:44:25 +08:00
def get_sources ( self ) :
return self . sources
2017-03-20 06:28:05 +08:00
def get_generated_lists ( self ) :
genlists = [ ]
2020-03-06 01:50:30 +08:00
for c in unholder ( self . sources ) :
2017-03-20 06:28:05 +08:00
if isinstance ( c , GeneratedList ) :
genlists . append ( c )
return genlists
2014-05-23 21:44:25 +08:00
def get_generated_sources ( self ) :
2017-03-20 06:28:05 +08:00
return self . get_generated_lists ( )
2014-05-23 21:44:25 +08:00
2018-05-19 20:36:04 +08:00
def get_dep_outname ( self , infilenames ) :
if self . depfile is None :
raise InvalidArguments ( ' Tried to get depfile name for custom_target that does not have depfile defined. ' )
2020-09-15 03:01:34 +08:00
if infilenames :
2018-05-19 20:36:04 +08:00
plainname = os . path . basename ( infilenames [ 0 ] )
basename = os . path . splitext ( plainname ) [ 0 ]
return self . depfile . replace ( ' @BASENAME@ ' , basename ) . replace ( ' @PLAINNAME@ ' , plainname )
else :
if ' @BASENAME@ ' in self . depfile or ' @PLAINNAME@ ' in self . depfile :
raise InvalidArguments ( ' Substitution in depfile for custom_target that does not have an input file. ' )
return self . depfile
2019-03-18 04:34:19 +08:00
def is_linkable_target ( self ) :
if len ( self . outputs ) != 1 :
return False
suf = os . path . splitext ( self . outputs [ 0 ] ) [ - 1 ]
2020-09-16 23:34:52 +08:00
if suf == ' .a ' or suf == ' .dll ' or suf == ' .lib ' or suf == ' .so ' or suf == ' .dylib ' :
2019-03-18 04:34:19 +08:00
return True
2019-03-19 06:26:43 +08:00
def get_link_deps_mapping ( self , prefix , environment ) :
return { }
2019-03-18 04:34:19 +08:00
def get_link_dep_subdirs ( self ) :
return OrderedSet ( )
def get_all_link_deps ( self ) :
return [ ]
2020-09-22 18:51:14 +08:00
def is_internal ( self ) - > bool :
if not self . should_install ( ) :
return True
for out in self . get_outputs ( ) :
# Can't check if this is a static library, so try to guess
if not out . endswith ( ( ' .a ' , ' .lib ' ) ) :
return False
return True
def extract_all_objects_recurse ( self ) :
return self . get_outputs ( )
2015-07-05 06:47:34 +08:00
def type_suffix ( self ) :
return " @cus "
2017-09-26 04:46:07 +08:00
def __getitem__ ( self , index ) :
return CustomTargetIndex ( self , self . outputs [ index ] )
def __setitem__ ( self , index , value ) :
raise NotImplementedError
def __delitem__ ( self , index ) :
raise NotImplementedError
2020-01-07 04:50:45 +08:00
def __iter__ ( self ) :
for i in self . outputs :
yield CustomTargetIndex ( self , i )
2017-01-14 22:46:37 +08:00
class RunTarget ( Target ) :
2017-09-07 06:21:50 +08:00
def __init__ ( self , name , command , args , dependencies , subdir , subproject ) :
2018-11-29 02:40:16 +08:00
self . typename = ' run '
2018-10-05 08:52:08 +08:00
# These don't produce output artifacts
super ( ) . __init__ ( name , subdir , subproject , False , MachineChoice . BUILD )
2014-06-12 03:38:36 +08:00
self . command = command
self . args = args
2016-06-25 04:07:57 +08:00
self . dependencies = dependencies
2014-06-12 03:38:36 +08:00
2016-07-01 17:13:51 +08:00
def __repr__ ( self ) :
repr_str = " < {0} {1} : {2} > "
return repr_str . format ( self . __class__ . __name__ , self . get_id ( ) , self . command )
2019-12-04 22:46:03 +08:00
def process_kwargs ( self , kwargs ) :
return self . process_kwargs_base ( kwargs )
2014-06-12 03:38:36 +08:00
def get_dependencies ( self ) :
2016-06-25 04:07:57 +08:00
return self . dependencies
2014-06-12 03:38:36 +08:00
def get_generated_sources ( self ) :
return [ ]
2015-05-03 20:06:04 +08:00
def get_sources ( self ) :
return [ ]
2020-09-01 20:28:08 +08:00
def should_install ( self ) - > bool :
2014-06-12 03:38:36 +08:00
return False
2020-09-01 20:28:08 +08:00
def get_filename ( self ) - > str :
2014-06-12 03:38:36 +08:00
return self . name
2014-08-13 03:51:36 +08:00
2020-09-01 20:28:08 +08:00
def get_outputs ( self ) - > T . List [ str ] :
2018-12-30 18:51:32 +08:00
if isinstance ( self . name , str ) :
return [ self . name ]
elif isinstance ( self . name , list ) :
return self . name
else :
raise RuntimeError ( ' RunTarget: self.name is neither a list nor a string. This is a bug ' )
2018-11-29 21:53:28 +08:00
2015-07-05 06:47:34 +08:00
def type_suffix ( self ) :
return " @run "
2019-07-04 01:32:24 +08:00
class AliasTarget ( RunTarget ) :
def __init__ ( self , name , dependencies , subdir , subproject ) :
super ( ) . __init__ ( name , ' ' , [ ] , dependencies , subdir , subproject )
2014-03-11 05:33:24 +08:00
class Jar ( BuildTarget ) :
2018-03-30 01:36:46 +08:00
known_kwargs = known_jar_kwargs
2018-10-05 08:52:08 +08:00
def __init__ ( self , name , subdir , subproject , for_machine : MachineChoice , sources , objects , environment , kwargs ) :
2018-12-30 18:31:03 +08:00
self . typename = ' jar '
2018-10-05 08:52:08 +08:00
super ( ) . __init__ ( name , subdir , subproject , for_machine , sources , objects , environment , kwargs )
2014-03-11 05:33:24 +08:00
for s in self . sources :
if not s . endswith ( ' .java ' ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' Jar source {} is not a java file. ' . format ( s ) )
2018-06-19 23:24:57 +08:00
for t in self . link_targets :
if not isinstance ( t , Jar ) :
2020-03-04 03:45:43 +08:00
raise InvalidArguments ( ' Link target {} is not a jar target. ' . format ( t ) )
2014-03-11 06:23:20 +08:00
self . filename = self . name + ' .jar '
Support multiple install dirs for built/custom targets
You can now pass a list of strings to the install_dir: kwarg to
build_target and custom_target.
Custom Targets:
===============
Allows you to specify the installation directory for each
corresponding output. For example:
custom_target('different-install-dirs',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : ['somedir', 'otherdir])
This would install first.file to somedir and second.file to otherdir.
If only one install_dir is provided, all outputs are installed there
(same behaviour as before).
To only install some outputs, pass `false` for the outputs that you
don't want installed. For example:
custom_target('only-install-second',
output : ['first.file', 'second.file'],
...
install : true,
install_dir : [false, 'otherdir])
This would install second.file to otherdir and not install first.file.
Build Targets:
==============
With build_target() (which includes executable(), library(), etc),
usually there is only one primary output. However some types of
targets have multiple outputs.
For example, while generating Vala libraries, valac also generates
a header and a .vapi file both of which often need to be installed.
This allows you to specify installation directories for those too.
# This will only install the library (same as before)
shared_library('somevalalib', 'somesource.vala',
...
install : true)
# This will install the library, the header, and the vapi into the
# respective directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : ['libdir', 'incdir', 'vapidir'])
# This will install the library into the default libdir and
# everything else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [true, 'incdir', 'vapidir'])
# This will NOT install the library, and will install everything
# else into the specified directories
shared_library('somevalalib', 'somesource.vala',
...
install : true,
install_dir : [false, 'incdir', 'vapidir'])
true/false can also be used for secondary outputs in the same way.
Valac can also generate a GIR file for libraries when the `vala_gir:`
keyword argument is passed to library(). In that case, `install_dir:`
must be given a list with four elements, one for each output.
Includes tests for all these.
Closes https://github.com/mesonbuild/meson/issues/705
Closes https://github.com/mesonbuild/meson/issues/891
Closes https://github.com/mesonbuild/meson/issues/892
Closes https://github.com/mesonbuild/meson/issues/1178
Closes https://github.com/mesonbuild/meson/issues/1193
2017-03-13 06:15:10 +08:00
self . outputs = [ self . filename ]
2016-11-20 06:26:45 +08:00
self . java_args = kwargs . get ( ' java_args ' , [ ] )
2014-03-11 05:33:24 +08:00
2014-03-12 04:30:05 +08:00
def get_main_class ( self ) :
return self . main_class
2015-07-05 06:47:34 +08:00
def type_suffix ( self ) :
return " @jar "
2016-11-20 06:26:45 +08:00
def get_java_args ( self ) :
return self . java_args
2018-10-05 08:52:08 +08:00
def validate_install ( self , environment ) :
2017-02-26 20:18:57 +08:00
# All jar targets are installable.
pass
2018-06-19 23:24:57 +08:00
def is_linkable_target ( self ) :
return True
def get_classpath_args ( self ) :
cp_paths = [ os . path . join ( l . get_subdir ( ) , l . get_filename ( ) ) for l in self . link_targets ]
2018-12-09 03:43:42 +08:00
cp_string = os . pathsep . join ( cp_paths )
if cp_string :
return [ ' -cp ' , os . pathsep . join ( cp_paths ) ]
return [ ]
2018-06-19 23:24:57 +08:00
2017-09-26 04:46:07 +08:00
class CustomTargetIndex :
2017-11-26 14:14:41 +08:00
""" A special opaque object returned by indexing a CustomTarget. This object
2017-09-26 04:46:07 +08:00
exists in meson , but acts as a proxy in the backends , making targets depend
on the CustomTarget it ' s derived from, but only adding one source file to
the sources .
"""
def __init__ ( self , target , output ) :
2018-11-29 02:40:16 +08:00
self . typename = ' custom '
2017-09-26 04:46:07 +08:00
self . target = target
self . output = output
2018-10-05 08:52:08 +08:00
self . for_machine = target . for_machine
2017-09-26 04:46:07 +08:00
def __repr__ ( self ) :
return ' <CustomTargetIndex: {!r} [ {} ]> ' . format (
2017-12-23 02:33:10 +08:00
self . target , self . target . get_outputs ( ) . index ( self . output ) )
2017-09-26 04:46:07 +08:00
2020-09-01 20:28:08 +08:00
def get_outputs ( self ) - > T . List [ str ] :
2017-09-26 04:46:07 +08:00
return [ self . output ]
def get_subdir ( self ) :
return self . target . get_subdir ( )
2019-03-28 00:18:39 +08:00
def get_filename ( self ) :
return self . output
def get_id ( self ) :
return self . target . get_id ( )
def get_all_link_deps ( self ) :
2019-04-09 22:43:40 +08:00
return self . target . get_all_link_deps ( )
2019-03-28 00:18:39 +08:00
def get_link_deps_mapping ( self , prefix , environment ) :
return self . target . get_link_deps_mapping ( prefix , environment )
def get_link_dep_subdirs ( self ) :
return self . target . get_link_dep_subdirs ( )
def is_linkable_target ( self ) :
suf = os . path . splitext ( self . output ) [ - 1 ]
if suf == ' .a ' or suf == ' .dll ' or suf == ' .lib ' or suf == ' .so ' :
return True
2020-09-22 18:51:14 +08:00
def should_install ( self ) - > bool :
return self . target . should_install ( )
def is_internal ( self ) - > bool :
return self . target . is_internal ( )
def extract_all_objects_recurse ( self ) :
return self . target . extract_all_objects_recurse ( )
2017-01-17 21:13:03 +08:00
class ConfigureFile :
2013-09-28 23:54:09 +08:00
def __init__ ( self , subdir , sourcename , targetname , configuration_data ) :
self . subdir = subdir
self . sourcename = sourcename
self . targetname = targetname
self . configuration_data = configuration_data
2016-07-01 17:13:51 +08:00
def __repr__ ( self ) :
repr_str = " < {0} : {1} -> {2} > "
src = os . path . join ( self . subdir , self . sourcename )
dst = os . path . join ( self . subdir , self . targetname )
return repr_str . format ( self . __class__ . __name__ , src , dst )
2013-09-28 23:54:09 +08:00
def get_configuration_data ( self ) :
return self . configuration_data
def get_subdir ( self ) :
return self . subdir
def get_source_name ( self ) :
return self . sourcename
def get_target_name ( self ) :
return self . targetname
2017-01-17 21:13:03 +08:00
class ConfigurationData :
2020-03-03 03:11:10 +08:00
def __init__ ( self ) - > None :
2013-09-28 23:54:09 +08:00
super ( ) . __init__ ( )
2020-03-03 03:11:17 +08:00
self . values = { } # T.Dict[str, T.Union[str, int, bool]]
2013-09-28 23:54:09 +08:00
2016-07-01 17:13:51 +08:00
def __repr__ ( self ) :
return repr ( self . values )
2020-03-03 03:11:17 +08:00
def __contains__ ( self , value : str ) - > bool :
2017-01-06 00:17:47 +08:00
return value in self . values
2020-03-03 03:11:17 +08:00
def get ( self , name : str ) - > T . Tuple [ T . Union [ str , int , bool ] , T . Optional [ str ] ] :
2017-01-06 00:17:47 +08:00
return self . values [ name ] # (val, desc)
2013-09-28 23:54:09 +08:00
2020-03-03 03:11:17 +08:00
def keys ( self ) - > T . Iterator [ str ] :
2013-09-28 23:54:09 +08:00
return self . values . keys ( )
2014-02-06 06:16:23 +08:00
2015-12-23 21:10:27 +08:00
# A bit poorly named, but this represents plain data files to copy
# during install.
2017-01-17 21:13:03 +08:00
class Data :
2021-01-13 02:50:22 +08:00
def __init__ ( self , sources : T . List [ File ] , install_dir : str ,
install_mode : T . Optional [ ' FileMode ' ] = None , rename : T . List [ str ] = None ) :
2015-12-23 21:10:27 +08:00
self . sources = sources
self . install_dir = install_dir
2017-01-14 17:11:18 +08:00
self . install_mode = install_mode
2018-03-12 08:43:34 +08:00
if rename is None :
self . rename = [ os . path . basename ( f . fname ) for f in self . sources ]
else :
2021-01-13 02:50:22 +08:00
self . rename = rename
2015-07-30 07:01:47 +08:00
2016-12-20 04:31:24 +08:00
class RunScript ( dict ) :
2016-12-18 22:29:48 +08:00
def __init__ ( self , script , args ) :
2016-12-20 04:31:24 +08:00
super ( ) . __init__ ( )
2016-12-18 22:29:48 +08:00
assert ( isinstance ( script , list ) )
assert ( isinstance ( args , list ) )
self [ ' exe ' ] = script
self [ ' args ' ] = args
2016-12-27 02:56:04 +08:00
class TestSetup :
2020-01-06 22:27:38 +08:00
def __init__ ( self , exe_wrapper : T . Optional [ T . List [ str ] ] , gdb : bool ,
2019-05-15 07:36:10 +08:00
timeout_multiplier : int , env : EnvironmentVariables ) :
2016-12-27 02:56:04 +08:00
self . exe_wrapper = exe_wrapper
self . gdb = gdb
self . timeout_multiplier = timeout_multiplier
self . env = env
2017-02-13 23:41:03 +08:00
2019-08-18 02:12:56 +08:00
def get_sources_string_names ( sources , backend ) :
2017-02-13 23:41:03 +08:00
'''
For the specified list of @sources which can be strings , Files , or targets ,
get all the output basenames .
'''
names = [ ]
2020-03-06 01:50:30 +08:00
for s in unholder ( sources ) :
2017-02-13 23:41:03 +08:00
if isinstance ( s , str ) :
names . append ( s )
2018-05-01 19:51:55 +08:00
elif isinstance ( s , ( BuildTarget , CustomTarget , CustomTargetIndex , GeneratedList ) ) :
2017-02-13 23:41:03 +08:00
names + = s . get_outputs ( )
2019-08-18 02:12:56 +08:00
elif isinstance ( s , ExtractedObjects ) :
names + = s . get_outputs ( backend )
2017-02-13 23:41:03 +08:00
elif isinstance ( s , File ) :
names . append ( s . fname )
else :
raise AssertionError ( ' Unknown source type: {!r} ' . format ( s ) )
return names
2018-03-01 06:25:12 +08:00
2019-05-15 07:39:15 +08:00
def load ( build_dir : str ) - > Build :
2018-03-01 06:25:12 +08:00
filename = os . path . join ( build_dir , ' meson-private ' , ' build.dat ' )
load_fail_msg = ' Build data file {!r} is corrupted. Try with a fresh build tree. ' . format ( filename )
nonexisting_fail_msg = ' No such build data file as " {!r} " . ' . format ( filename )
try :
with open ( filename , ' rb ' ) as f :
obj = pickle . load ( f )
except FileNotFoundError :
raise MesonException ( nonexisting_fail_msg )
2019-03-12 02:57:58 +08:00
except ( pickle . UnpicklingError , EOFError ) :
2018-03-01 06:25:12 +08:00
raise MesonException ( load_fail_msg )
2019-03-12 02:57:58 +08:00
except AttributeError :
raise MesonException (
" Build data file {!r} references functions or classes that don ' t "
" exist. This probably means that it was generated with an old "
2019-11-30 21:24:40 +08:00
" version of meson. Try running from the source directory "
" meson {} --wipe " . format ( filename , build_dir ) )
2018-03-01 06:25:12 +08:00
if not isinstance ( obj , Build ) :
raise MesonException ( load_fail_msg )
return obj
2020-09-01 20:28:08 +08:00
def save ( obj : Build , filename : str ) - > None :
2018-03-01 06:25:12 +08:00
with open ( filename , ' wb ' ) as f :
pickle . dump ( obj , f )