2015-12-17 08:43:23 +08:00
# Copyright 2015 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
2018-10-01 00:11:11 +08:00
import enum
2017-01-09 04:47:57 +08:00
import os
2018-10-01 00:11:11 +08:00
import re
2017-01-09 04:47:57 +08:00
2017-03-28 17:01:45 +08:00
from . . import mlog
2018-06-29 22:03:17 +08:00
from . . import mesonlib , build
2017-08-30 04:21:11 +08:00
from . . mesonlib import MesonException , extract_as_list
2016-12-29 06:02:03 +08:00
from . import get_include_args
2017-01-07 02:11:16 +08:00
from . import ModuleReturnValue
2017-01-09 04:47:57 +08:00
from . import ExtensionModule
2018-06-05 00:40:13 +08:00
from . . interpreter import CustomTargetHolder
2018-06-02 05:14:42 +08:00
from . . interpreterbase import permittedKwargs , FeatureNewKwargs
2018-06-29 22:03:17 +08:00
from . . dependencies import ExternalProgram
2017-01-07 02:11:16 +08:00
2018-10-01 00:11:11 +08:00
class ResourceCompilerType ( enum . Enum ) :
windres = 1
rc = 2
2017-01-09 04:47:57 +08:00
class WindowsModule ( ExtensionModule ) :
2015-12-17 08:43:23 +08:00
def detect_compiler ( self , compilers ) :
2016-12-09 15:17:14 +08:00
for l in ( ' c ' , ' cpp ' ) :
if l in compilers :
return compilers [ l ]
2015-12-17 08:43:23 +08:00
raise MesonException ( ' Resource compilation requires a C or C++ compiler. ' )
2018-09-30 23:43:32 +08:00
def _find_resource_compiler ( self , state ) :
# FIXME: Does not handle `native: true` executables, see
# See https://github.com/mesonbuild/meson/issues/1531
2018-08-10 04:06:55 +08:00
# But given a machine, we can un-hardcode `binaries.host` below.
2016-12-29 06:02:03 +08:00
2018-09-30 23:43:32 +08:00
if hasattr ( self , ' _rescomp ' ) :
return self . _rescomp
2016-12-29 06:02:03 +08:00
2018-08-10 04:06:55 +08:00
# Will try cross / native file and then env var
rescomp = ExternalProgram . from_bin_list ( state . environment . binaries . host , ' windres ' )
2018-05-12 00:07:34 +08:00
2018-08-31 22:51:18 +08:00
if not rescomp or not rescomp . found ( ) :
2018-09-30 23:43:32 +08:00
comp = self . detect_compiler ( state . compilers )
2018-10-23 23:14:40 +08:00
if comp . id == ' msvc ' or comp . id == ' clang-cl ' :
2018-08-31 22:51:18 +08:00
rescomp = ExternalProgram ( ' rc ' , silent = True )
else :
rescomp = ExternalProgram ( ' windres ' , silent = True )
if not rescomp . found ( ) :
raise MesonException ( ' Could not find Windows resource compiler ' )
2018-10-01 00:11:11 +08:00
for ( arg , match , type ) in [
( ' /? ' , ' ^.*Microsoft.*Resource Compiler.*$ ' , ResourceCompilerType . rc ) ,
( ' --version ' , ' ^.*GNU windres.*$ ' , ResourceCompilerType . windres ) ,
] :
p , o , e = mesonlib . Popen_safe ( rescomp . get_command ( ) + [ arg ] )
m = re . search ( match , o , re . MULTILINE )
if m :
mlog . log ( ' Windows resource compiler: %s ' % m . group ( ) )
self . _rescomp = ( rescomp , type )
break
else :
raise MesonException ( ' Could not determine type of Windows resource compiler ' )
2018-09-30 23:43:32 +08:00
return self . _rescomp
@FeatureNewKwargs ( ' windows.compile_resources ' , ' 0.47.0 ' , [ ' depend_files ' , ' depends ' ] )
@permittedKwargs ( { ' args ' , ' include_directories ' , ' depend_files ' , ' depends ' } )
def compile_resources ( self , state , args , kwargs ) :
extra_args = mesonlib . stringlistify ( kwargs . get ( ' args ' , [ ] ) )
wrc_depend_files = extract_as_list ( kwargs , ' depend_files ' , pop = True )
wrc_depends = extract_as_list ( kwargs , ' depends ' , pop = True )
for d in wrc_depends :
if isinstance ( d , CustomTargetHolder ) :
extra_args + = get_include_args ( [ d . outdir_include ( ) ] )
inc_dirs = extract_as_list ( kwargs , ' include_directories ' , pop = True )
for incd in inc_dirs :
if not isinstance ( incd . held_object , ( str , build . IncludeDirs ) ) :
raise MesonException ( ' Resource include dirs should be include_directories(). ' )
extra_args + = get_include_args ( inc_dirs )
2018-10-01 00:11:11 +08:00
rescomp , rescomp_type = self . _find_resource_compiler ( state )
if rescomp_type == ResourceCompilerType . rc :
2018-08-31 22:51:18 +08:00
# RC is used to generate .res files, a special binary resource
# format, which can be passed directly to LINK (apparently LINK uses
# CVTRES internally to convert this to a COFF object)
2015-12-17 08:43:23 +08:00
suffix = ' res '
2018-08-31 22:51:18 +08:00
res_args = extra_args + [ ' /nologo ' , ' /fo@OUTPUT@ ' , ' @INPUT@ ' ]
2015-12-17 08:43:23 +08:00
else :
2018-08-31 22:51:18 +08:00
# ld only supports object files, so windres is used to generate a
# COFF object
suffix = ' o '
res_args = extra_args + [ ' @INPUT@ ' , ' @OUTPUT@ ' ]
2017-03-28 17:01:45 +08:00
m = ' Argument {!r} has a space which may not work with windres due to ' \
' a MinGW bug: https://sourceware.org/bugzilla/show_bug.cgi?id=4933 '
for arg in extra_args :
if ' ' in arg :
mlog . warning ( m . format ( arg ) )
2017-12-22 04:36:30 +08:00
2017-12-23 02:16:56 +08:00
res_targets = [ ]
2017-12-22 04:36:30 +08:00
2017-12-23 02:16:56 +08:00
def add_target ( src ) :
if isinstance ( src , list ) :
for subsrc in src :
add_target ( subsrc )
return
2017-12-22 04:36:30 +08:00
2017-12-23 02:16:56 +08:00
if hasattr ( src , ' held_object ' ) :
src = src . held_object
2018-06-08 01:01:39 +08:00
if isinstance ( src , str ) :
2018-08-15 03:13:33 +08:00
name_format = ' file {!r} '
2018-08-26 21:19:05 +08:00
name = os . path . join ( state . subdir , src )
2018-06-08 01:01:39 +08:00
elif isinstance ( src , mesonlib . File ) :
2018-08-15 03:13:33 +08:00
name_format = ' file {!r} '
2018-08-26 21:19:05 +08:00
name = src . relative_name ( )
2017-12-23 02:16:56 +08:00
elif isinstance ( src , build . CustomTarget ) :
if len ( src . get_outputs ( ) ) > 1 :
raise MesonException ( ' windows.compile_resources does not accept custom targets with more than 1 output. ' )
2018-08-15 03:13:33 +08:00
name_format = ' target {!r} '
2018-08-26 21:19:05 +08:00
name = src . get_id ( )
2017-12-23 02:16:56 +08:00
else :
raise MesonException ( ' Unexpected source type {!r} . windows.compile_resources accepts only strings, files, custom targets, and lists thereof. ' . format ( src ) )
# Path separators are not allowed in target names
name = name . replace ( ' / ' , ' _ ' ) . replace ( ' \\ ' , ' _ ' )
2018-08-15 03:13:33 +08:00
res_kwargs = {
' output ' : name + ' _@BASENAME@. ' + suffix ,
' input ' : [ src ] ,
' command ' : [ rescomp ] + res_args ,
' depend_files ' : wrc_depend_files ,
' depends ' : wrc_depends ,
}
2018-05-15 22:13:06 +08:00
# instruct binutils windres to generate a preprocessor depfile
2018-10-01 00:11:11 +08:00
if rescomp_type == ResourceCompilerType . windres :
2018-05-15 22:13:06 +08:00
res_kwargs [ ' depfile ' ] = res_kwargs [ ' output ' ] + ' .d '
res_kwargs [ ' command ' ] + = [ ' --preprocessor-arg=-MD ' , ' --preprocessor-arg=-MQ@OUTPUT@ ' , ' --preprocessor-arg=-MF@DEPFILE@ ' ]
2018-08-15 03:13:33 +08:00
res_targets . append ( build . CustomTarget ( ' Windows resource for ' + name_format . format ( name ) , state . subdir , state . subproject , res_kwargs ) )
2017-12-23 02:16:56 +08:00
add_target ( args )
return ModuleReturnValue ( res_targets , [ res_targets ] )
2015-12-17 08:43:23 +08:00
2018-03-18 03:09:48 +08:00
def initialize ( * args , * * kwargs ) :
return WindowsModule ( * args , * * kwargs )