2010-03-30 12:27:15 +00:00
EnsureSConsVersion(1,2)
2019-10-06 16:15:14 +00:00
EnsurePythonVersion(2,7)
2014-02-08 00:13:52 +00:00
2008-11-15 21:54:24 +00:00
stubs = [
'bzip2',
'lzma',
'zlib'
]
2008-12-12 16:33:25 +00:00
plugin_libs = [
'ExDLL'
]
2008-11-15 21:54:24 +00:00
plugins = [
'AdvSplash',
'Banner',
'BgImage',
'Dialer',
'InstallOptions',
'LangDLL',
'Library/TypeLib',
'Math',
'nsDialogs',
'nsExec',
'NSISdl',
'Splash',
'StartMenu',
'System',
'UserInfo',
2008-12-12 16:33:25 +00:00
'VPatch/Source/Plugin',
2008-11-15 21:54:24 +00:00
]
utils = [
'Library/RegTool',
'MakeLangId',
'Makensisw',
'NSIS Menu',
'UIs',
2010-02-07 21:24:09 +00:00
'SubStart',
2008-11-15 21:54:24 +00:00
'VPatch/Source/GenPat',
'zip2exe'
]
misc = [
'Graphics',
'Language files',
'MultiUser',
'Modern UI',
'Modern UI 2',
2008-12-12 16:33:25 +00:00
'VPatch'
2008-11-15 21:54:24 +00:00
]
doc = [
'COPYING'
]
2011-12-02 23:18:34 +00:00
doctypes = [
'chm',
'html',
2020-11-02 23:58:19 +00:00
'htmlsingle',
'web',
2011-12-02 23:18:34 +00:00
]
2008-11-15 21:54:24 +00:00
######################################################################
####### Build Environment ###
######################################################################
2020-06-26 22:52:09 +00:00
import os
2008-11-15 21:54:24 +00:00
path = ARGUMENTS.get('PATH', '')
toolset = ARGUMENTS.get('TOOLSET', '')
2020-06-26 22:52:09 +00:00
defenv = {
'TARGET_ARCH': ARGUMENTS.get('TARGET_ARCH', 'x86'),
'ENV': {}
}
if path: defenv['ENV']['PATH'] = path
if toolset: defenv['TOOLS'] = toolset.split(',') + ['zip']
2008-11-15 21:54:24 +00:00
2020-06-26 22:52:09 +00:00
if len(defenv['ENV']) == 0: del defenv['ENV']
defenv = Environment(**defenv)
2008-11-15 21:54:24 +00:00
Export('defenv')
######################################################################
####### Includes ###
######################################################################
SConscript('SCons/utils.py')
2020-06-26 22:52:09 +00:00
Import('GetOptionOrEnv')
2008-11-15 21:54:24 +00:00
######################################################################
####### Options ###
######################################################################
2011-12-02 23:18:34 +00:00
default_doctype = 'html'
2008-11-15 21:54:24 +00:00
if defenv.WhereIs('hhc', os.environ['PATH']):
2011-12-02 23:18:34 +00:00
default_doctype = 'chm'
2008-11-15 21:54:24 +00:00
2021-01-15 16:31:10 +00:00
import time
cvs_version = time.strftime('%d-%b-%Y.cvs', time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
2008-11-15 21:54:24 +00:00
2008-12-24 23:40:38 +00:00
opts = Variables()
2008-11-15 21:54:24 +00:00
# load configuration options
# it's important this will be done here so NSIS_CONFIG_CONST_DATA_PATH
# will be available for the next few lines and so `dirs` can be set
SConscript('SCons/config.py')
opts.Update(defenv)
Help(opts.GenerateHelpText(defenv))
2011-12-15 23:35:40 +00:00
#Fix ListVariable to generate comma separated (quoted if required) list of allowed names:
org_ListVariable = ListVariable
def ListVariable(key, help, default, names, map={}):
r = list(org_ListVariable(key, help, default, names, map))
if len(r) == 5 and isinstance(r[1], str):
fmt = 'allowed names: %s'
repl = fmt % ', '.join( ((' ' in s) and ('"%s"' % s) or s) for s in names )
r[1] = r[1].replace(fmt % ' '.join(names), repl)
return r
2008-11-15 21:54:24 +00:00
install_dirs = {
'relocatable': {
'dest': '',
'prefix': '',
'conf': '$PREFIX',
'bin': '$PREFIX',
'data': '$PREFIX',
2008-12-12 16:33:25 +00:00
'doc': '$PREFIX'
2008-11-15 21:54:24 +00:00
},
'static': {
'dest': '',
'prefix': '/usr/local',
'conf': '$PREFIX/etc',
'bin': '$PREFIX/bin',
'data': '$PREFIX/share/nsis',
2008-12-12 16:33:25 +00:00
'doc': '$PREFIX/share/doc/nsis'
2008-11-15 21:54:24 +00:00
}
}
if 'NSIS_CONFIG_CONST_DATA_PATH' in defenv['NSIS_CPPDEFINES']:
dirs = install_dirs['static']
else:
dirs = install_dirs['relocatable']
2018-11-02 20:15:20 +00:00
if defenv['PLATFORM'] == 'win32':
2008-11-15 21:54:24 +00:00
ignore_tests = 'none'
else:
ignore_tests = ','.join(Split("""
2018-11-01 21:20:53 +00:00
Examples/makensis.nsi"""))
2008-11-15 21:54:24 +00:00
# version
opts.Add(('VERSION', 'Version of NSIS', cvs_version))
opts.Add(('VER_MAJOR', 'Major version of NSIS (recommended for dist-installer)', None))
opts.Add(('VER_MINOR', 'Minor version of NSIS (recommended for dist-installer)', None))
2011-12-15 20:07:37 +00:00
opts.Add(('VER_REVISION', 'Revision of NSIS', None))
opts.Add(('VER_BUILD', 'Build version of NSIS', None))
opts.Add(('VER_PACKED', 'Packed version of NSIS in 0xMMmmmrrb format, used for feature detection in scripts (recommended if VER_MAJOR and VER_MINOR are not set)', None))
2008-11-15 21:54:24 +00:00
# installation
opts.Add(('PREFIX', 'Installation prefix', dirs['prefix']))
2008-12-24 23:40:38 +00:00
opts.Add(ListVariable('SKIPSTUBS', 'A list of stubs that will not be built', 'none', stubs))
2014-10-05 21:08:15 +00:00
opts.Add(ListVariable('SKIPPLUGINS', 'A list of plug-ins that will not be built', 'none', plugin_libs + plugins))
2018-10-30 22:33:18 +00:00
opts.Add(ListVariable('SKIPUTILS', 'A list of utilities that will not be built', 'NSIS Menu', utils))
2008-12-24 23:40:38 +00:00
opts.Add(ListVariable('SKIPMISC', 'A list of plug-ins that will not be built', 'none', misc))
opts.Add(ListVariable('SKIPDOC', 'A list of doc files that will not be built/installed', 'none', doc))
2008-11-15 21:54:24 +00:00
opts.Add(('SKIPTESTS', 'A comma-separated list of test files that will not be ran', 'none'))
opts.Add(('IGNORETESTS', 'A comma-separated list of test files that will be ran but ignored', ignore_tests))
# build tools
opts.Add(('PATH', 'A colon-separated list of system paths instead of the default - TEMPORARY AND MAY DEPRECATE', None))
opts.Add(('TOOLSET', 'A comma-separated list of specific tools used for building instead of the default', None))
2008-12-24 23:40:38 +00:00
opts.Add(BoolVariable('MSTOOLKIT', 'Use Microsoft Visual C++ Toolkit', 'no'))
2010-07-04 00:07:59 +00:00
opts.Add(EnumVariable('MSVS_VERSION', 'MS Visual C++ version', os.environ.get('MSVS_VERSION'), allowed_values=('6.0', '7.0', '7.1', '8.0', '8.0Exp', '9.0', '9.0Exp', '10.0', '10.0Exp')))
2018-06-03 21:00:53 +00:00
opts.Add(EnumVariable('TARGET_ARCH', 'Target processor architecture', 'x86', allowed_values=('x86', 'amd64', 'arm64')))
2011-12-02 23:18:34 +00:00
opts.Add(ListVariable('DOCTYPES', 'A list of document types that will be built', default_doctype, doctypes))
2014-10-05 23:51:09 +00:00
opts.Add(('CC', 'Override C compiler', None))
opts.Add(('CXX', 'Override C++ compiler', None))
2008-12-24 23:40:38 +00:00
opts.Add(PathVariable('APPEND_CPPPATH', 'Additional paths to search for include files', None))
opts.Add(PathVariable('APPEND_LIBPATH', 'Additional paths to search for libraries', None))
2008-11-15 21:54:24 +00:00
opts.Add(('APPEND_CCFLAGS', 'Additional C/C++ compiler flags'))
opts.Add(('APPEND_LINKFLAGS', 'Additional linker flags'))
2009-11-29 23:19:58 +00:00
opts.Add(PathVariable('WXWIN', 'Path to wxWindows library folder (e.g. C:\\Dev\\wxWidgets-2.8.10)', os.environ.get('WXWIN')))
2010-02-07 21:24:09 +00:00
opts.Add(PathVariable('ZLIB_W32', 'Path to Win32 zlib library folder (e.g. C:\\Dev\\zlib-1.2.3)', os.environ.get('ZLIB_W32')))
2008-11-15 21:54:24 +00:00
# build options
2012-09-21 14:28:24 +00:00
opts.Add(BoolVariable('UNICODE', 'Build the Unicode version of the compiler and tools', 'yes'))
2008-12-24 23:40:38 +00:00
opts.Add(BoolVariable('DEBUG', 'Build executables with debugging information', 'no'))
opts.Add(PathVariable('CODESIGNER', 'A program used to sign executables', None))
opts.Add(BoolVariable('STRIP', 'Strips executables of any unrequired data such as symbols', 'yes'))
opts.Add(BoolVariable('STRIP_CP', 'Strips cross-platform executables of any unrequired data such as symbols', 'yes'))
opts.Add(BoolVariable('STRIP_W32', 'Strips Win32 executables of any unrequired data such as symbols', 'yes'))
2008-11-15 21:54:24 +00:00
# path related build options
opts.Add(('PREFIX_DEST', 'Intermediate installation prefix (extra install time prefix)', dirs['dest']))
opts.Add(('PREFIX_CONF', 'Path to install nsisconf.nsh to', dirs['conf']))
opts.Add(('PREFIX_BIN', 'Path to install native binaries to', dirs['bin']))
opts.Add(('PREFIX_DATA', 'Path to install nsis data to (plugins, includes, stubs, contrib, win32 binaries)', dirs['data']))
opts.Add(('PREFIX_DOC','Path to install nsis README / INSTALL / TODO files to.', dirs['doc']))
2009-01-11 09:48:07 +00:00
opts.Add(('PREFIX_PLUGINAPI_INC','Path to install plugin API headers to.', None))
opts.Add(('PREFIX_PLUGINAPI_LIB','Path to install plugin static library to.', None))
2020-06-26 22:52:09 +00:00
# reproducible builds
opts.Add(('SOURCE_DATE_EPOCH', 'UNIX timestamp (in seconds)', os.environ.get('SOURCE_DATE_EPOCH')))
2008-11-15 21:54:24 +00:00
opts.Update(defenv)
Help(opts.GenerateHelpText(defenv))
2013-09-06 23:48:59 +00:00
if defenv['TARGET_ARCH'] != 'x86':
defenv['UNICODE'] = True
2014-06-20 18:36:05 +00:00
if defenv['DEBUG']:
defenv.Append(CPPDEFINES = ['DEBUG'])
2008-11-15 21:54:24 +00:00
# add prefixes defines
2017-05-14 16:16:30 +00:00
if 'NSIS_CONFIG_CONST_DATA_PATH' in defenv['NSIS_CPPDEFINES']:
defenv.Append(NSIS_CPPDEFINES = [('PREFIX_CONF', '"%s"' % defenv.subst('$PREFIX_CONF'))])
defenv.Append(NSIS_CPPDEFINES = [('PREFIX_DATA', '"%s"' % defenv.subst('$PREFIX_DATA'))])
defenv.Append(NSIS_CPPDEFINES = [('PREFIX_DOC', '"%s"' % defenv.subst('$PREFIX_DOC'))])
2020-08-11 23:14:38 +00:00
if defenv.get('SOURCE_DATE_EPOCH','') != '':
2020-06-26 22:52:09 +00:00
defenv['ENV']['SOURCE_DATE_EPOCH'] = defenv['SOURCE_DATE_EPOCH'] = int(defenv['SOURCE_DATE_EPOCH'], 0) # Normalize and apply to ENV for child processes
defenv.Append(NSIS_CPPDEFINES = [('NSIS_SOURCE_DATE_EPOCH', '%s' % defenv['SOURCE_DATE_EPOCH'])]) # Display in /HDRINFO
2017-05-14 16:16:30 +00:00
# Need this early for the config header files to be placed in
if defenv['UNICODE']:
2013-12-08 14:34:38 +00:00
if defenv['DEBUG']:
defenv.Replace(BUILD_PREFIX = 'build/udebug')
else:
defenv.Replace(BUILD_PREFIX = 'build/urelease')
2009-02-04 14:05:48 +00:00
else:
2013-12-08 14:34:38 +00:00
if defenv['DEBUG']:
defenv.Replace(BUILD_PREFIX = 'build/debug')
else:
defenv.Replace(BUILD_PREFIX = 'build/release')
2009-02-04 14:05:48 +00:00
defenv.Replace(BUILD_CONFIG = defenv.subst('$BUILD_PREFIX/config'))
# ensure the config directory exists
2011-12-03 00:14:44 +00:00
if not Dir(defenv.Dir('#$BUILD_CONFIG')).exists():
defenv.Execute(Mkdir(defenv.Dir('#$BUILD_CONFIG')))
2009-02-04 14:05:48 +00:00
2008-11-15 21:54:24 +00:00
# write configuration into sconf.h and defines.h
2009-02-04 14:05:48 +00:00
sconf_h = open(defenv.File('#$BUILD_CONFIG/nsis-sconf.h').abspath, 'w')
2008-11-15 21:54:24 +00:00
sconf_h.write('// This file is automatically generated by SCons\n// DO NOT EDIT THIS FILE\n')
2009-02-04 14:05:48 +00:00
defines_h = open(defenv.File('#$BUILD_CONFIG/nsis-defines.h').abspath, 'w')
2008-11-15 21:54:24 +00:00
defines_h.write('// This file is automatically generated by SCons\n// DO NOT EDIT THIS FILE\n')
for i in defenv['NSIS_CPPDEFINES']:
if type(i) is not str:
sconf_h.write('#define %s %s\n' % (i[0], i[1]))
if str(i[1])[0] != '"':
2010-03-24 17:22:56 +00:00
defines_h.write('definedlist.add(_T("%s"), _T("%s"));\n' % (i[0], i[1]))
2008-11-15 21:54:24 +00:00
else:
2010-03-24 17:22:56 +00:00
defines_h.write('definedlist.add(_T("%s"), _T(%s));\n' % (i[0], i[1]))
2008-11-15 21:54:24 +00:00
else:
sconf_h.write('#define %s\n' % (i))
2010-03-24 17:22:56 +00:00
defines_h.write('definedlist.add(_T("%s"));\n' % (i))
2008-11-15 21:54:24 +00:00
sconf_h.close()
defines_h.close()
# write version into version.h
2009-02-04 14:05:48 +00:00
f = open(defenv.File('#$BUILD_CONFIG/nsis-version.h').abspath, 'w')
2008-11-15 21:54:24 +00:00
f.write('// This file is automatically generated by SCons\n// DO NOT EDIT THIS FILE\n')
2013-12-08 14:34:38 +00:00
f.write('#include "%s"\n' % File('#/Source/tchar.h').abspath)
2010-03-24 17:22:56 +00:00
2019-10-06 16:15:14 +00:00
if (not 'VER_PACKED' in defenv) and 'VER_MAJOR' in defenv and 'VER_MINOR' in defenv:
2018-12-06 01:12:30 +00:00
packed_r = int(defenv.get('VER_REVISION','0'))
packed_b = int(defenv.get('VER_BUILD','0'))
defenv['VER_PACKED'] = '0x%0.2i%0.3i%0.2i%0.1i' % (int(defenv['VER_MAJOR']), int(defenv['VER_MINOR']), packed_r, packed_b)
2019-10-06 16:15:14 +00:00
if not 'VER_PACKED' in defenv:
2018-12-06 01:12:30 +00:00
import re
found = None
2019-10-06 16:15:14 +00:00
for v in re.compile(r'^\\H\{[v]?(\S+)\}', re.M).finditer(File('#/Docs/src/history.but').get_contents().decode()): # Try to parse the Halibut history file
2018-12-06 01:12:30 +00:00
if v and not found:
v = v.group(1).split('.')
if len(v) >= 2:
mi = int(re.search(r'\d+', v[1]).group())
if mi < 1: mi = 1 # Make sure we can subtract 1 from the minor number so trunk stays below the next release
defenv['VER_PACKED'] = '0x%0.2i%0.3i%0.2i%0.1i' % (int(re.search(r'\d+', v[0]).group()), mi - 1, 66, 6)
if int(defenv['VER_PACKED'], 0) >= int('0x03000000', 0):
found = v
if not found:
defenv['VER_PACKED'] = '0x%0.2i%0.3i%0.2i%0.1i' % (3, 3, 42, 0) # Default to a version number we never used
print('WARNING: VER_PACKED not set, defaulting to %s!' % defenv['VER_PACKED'])
if int(defenv['VER_PACKED'], 0) < int('0x03000000', 0) or int(defenv['VER_PACKED'], 0) >= int('0x04000000', 0):
print('ERROR: Invalid VER_PACKED value!')
Exit(1)
f.write('#define NSIS_PACKEDVERSION _T("%s")\n' % defenv['VER_PACKED'])
2011-12-15 20:07:37 +00:00
2020-04-20 17:48:16 +00:00
if defenv.get('VERSION','') == '' and 'VER_MAJOR' in defenv:
2010-03-24 17:22:56 +00:00
defenv['VERSION'] = defenv['VER_MAJOR']
2019-10-06 16:15:14 +00:00
if 'VER_MINOR' in defenv:
2010-03-24 17:22:56 +00:00
defenv['VERSION'] += '.' + defenv['VER_MINOR']
2019-10-06 16:15:14 +00:00
if 'VER_REVISION' in defenv:
2010-03-24 17:22:56 +00:00
defenv['VERSION'] += '.' + defenv['VER_REVISION']
2020-04-20 17:48:16 +00:00
if defenv.get('VERSION','') == '' and int(defenv['VER_PACKED'], 0) > int('0x02000000', 0):
2020-06-06 14:14:02 +00:00
defenv['VERSION'] = '%i.%i.%i' % (int(defenv['VER_PACKED'][2:][:2]), int(defenv['VER_PACKED'][4:][:3]), int(defenv['VER_PACKED'][7:][:2]))
2020-04-20 17:48:16 +00:00
print('WARNING: VERSION not set, defaulting to %s!' % defenv['VERSION'])
2010-03-24 17:22:56 +00:00
f.write('#define NSIS_VERSION _T("v%s")\n' % defenv['VERSION'])
2008-11-15 21:54:24 +00:00
f.close()
2012-10-13 01:47:50 +00:00
######################################################################
####### Common Functions ###
######################################################################
2018-06-03 21:00:53 +00:00
def GetArcCPU(env):
2019-10-06 16:15:14 +00:00
if (not 'TARGET_ARCH' in env) or env['TARGET_ARCH'] == 'x86':
2018-06-03 21:00:53 +00:00
return 'x86'
return env['TARGET_ARCH']
def GetArcSuffix(env, unicode = None):
if unicode is None:
unicode = 'UNICODE' in env['CPPDEFINES']
suff = '-unicode'
if not unicode:
suff = '-ansi'
return GetArcCPU(env) + suff
2012-10-13 01:47:50 +00:00
def SafeFile(f):
from types import StringType
if isinstance(f, StringType):
return File(f)
return f
def MakeFileList(files):
return Flatten(File(files))
def AddEnvStandardFlags(env, defines=None, flags=None, libs=None, entry=None, nodeflib=None):
if defines:
env.Append(CPPDEFINES = defines)
if flags:
env.Append(CCFLAGS = flags)
if libs:
env.Append(LIBS = libs)
if entry:
unicodestr = "None"
if 'UNICODE' in env['CPPDEFINES']:
unicodestr = "True"
env.Append(LINKFLAGS = ['${ENTRY_FLAG("%s",%s)}' % (entry,unicodestr)])
if nodeflib:
env.Append(LINKFLAGS = ['$NODEFLIBS_FLAG']) # no default libraries
def AppendRES(env, source, res, resources):
if res:
target = MakeFileList(res)[0].name.replace('.rc', '-rc')
target_res = env.RES(target, res)
if resources:
env.Depends(target_res, resources)
source.append(target_res)
def CleanMap(env, target, target_name):
env.Clean(target, File(target_name + '.map'))
2008-11-15 21:54:24 +00:00
######################################################################
####### Functions ###
######################################################################
defenv['ZIPDISTDIR'] = defenv.Dir('#nsis-$VERSION')
defenv['INSTDISTDIR'] = defenv.Dir('#.instdist')
defenv['TESTDISTDIR'] = defenv.Dir('#.test')
defenv['DISTSUFFIX'] = ''
2018-06-03 21:00:53 +00:00
if GetArcCPU(defenv) != 'x86':
defenv['DISTSUFFIX'] += GetArcCPU(defenv)
2019-10-06 16:15:14 +00:00
if 'CODESIGNER' in defenv:
2015-09-17 14:30:07 +00:00
defenv['DISTSUFFIX'] += '-signed'
2008-11-15 21:54:24 +00:00
defenv.Execute(Delete('$ZIPDISTDIR'))
defenv.Execute(Delete('$INSTDISTDIR'))
defenv.Execute(Delete('$TESTDISTDIR'))
def Distribute(files, names, component, path, subpath, alias, install_alias=None):
files = MakeFileList(files)
2020-03-24 02:35:17 +00:00
names = names or list(map(lambda x: x.name, files))
2019-10-06 16:15:14 +00:00
if isinstance(names, str):
2008-11-15 21:54:24 +00:00
names = [names]
for d in ('$ZIPDISTDIR', '$INSTDISTDIR', '$TESTDISTDIR'):
2019-10-06 16:15:14 +00:00
paths = list(map(lambda file: os.path.join(d, path, subpath, file), names))
2008-11-15 21:54:24 +00:00
defenv.InstallAs(paths, files)
2019-10-06 16:15:14 +00:00
if ('PREFIX' in defenv and defenv['PREFIX']) or ('PREFIX_DEST' in defenv and defenv['PREFIX_DEST']) :
2008-11-15 21:54:24 +00:00
prefix = '${PREFIX_DEST}${PREFIX_%s}' % component.upper()
2019-10-06 16:15:14 +00:00
paths = list(map(lambda file: os.path.join(prefix, path, subpath, file), names))
2008-11-15 21:54:24 +00:00
ins = defenv.InstallAs(paths, files)
else:
ins = []
if ins:
defenv.Alias('install', ins)
defenv.Alias('install-%s' % component, ins)
if alias:
defenv.Alias(alias, ins)
if install_alias:
defenv.Alias('install-%s' % install_alias, ins)
return ins
def DistributeBin(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'bin', '', path, alias)
def DistributeConf(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'conf', '', path, alias)
def DistributeW32Bin(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'data', 'Bin', path, alias, 'w32bin')
def DistributeStubs(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'data', 'Stubs', path, alias, 'stubs')
2012-10-13 01:47:50 +00:00
def DistributePlugin(files, names=[], arcsubpath='', alias=None):
return defenv.Distribute(files, names, 'data', 'Plugins', arcsubpath, alias, 'plugins')
2008-11-15 21:54:24 +00:00
def DistributeContrib(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'data', 'Contrib', path, alias, 'contrib')
def DistributeMenu(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'data', 'Menu', path, alias, 'menu')
def DistributeInclude(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'data', 'Include', path, alias, 'includes')
2011-12-02 23:18:34 +00:00
def DistributeDoc(files, names=[], path='', alias=None, basepath='', install_alias='docs'):
return defenv.Distribute(files, names, 'doc', path=basepath, subpath=path, alias=alias, install_alias=install_alias)
2008-11-15 21:54:24 +00:00
2011-12-02 23:18:34 +00:00
def DistributeDocs(files, names=[], path='', alias=None, basepath='Docs', install_alias='docs'):
return defenv.Distribute(files, names, 'doc', path=basepath, subpath=path, alias=alias, install_alias=install_alias)
2008-11-15 21:54:24 +00:00
def DistributeExamples(files, names=[], path='', alias=None):
return defenv.Distribute(files, names, 'doc', 'Examples', path, alias, 'examples')
2010-02-07 21:24:09 +00:00
def FindMakeNSIS(env, path):
exename = 'makensis_not_found'
file = env.FindFile('makensis$PROGSUFFIX',
[os.path.join(path, '.'), os.path.join(path, 'Bin')])
if file:
exename = str(file)
return exename
2008-11-15 21:54:24 +00:00
def Sign(targets):
2019-10-06 16:15:14 +00:00
if 'CODESIGNER' in defenv:
2008-11-15 21:54:24 +00:00
for t in targets:
a = defenv.Action('$CODESIGNER "%s"' % t.path)
defenv.AddPostAction(t, a)
2020-06-26 22:52:09 +00:00
Import('SilentActionEcho IsPEExecutable SetPESecurityFlagsWorker MakeReproducibleAction')
2017-10-26 16:37:42 +00:00
def SetPESecurityFlagsAction(target, source, env):
2017-10-25 23:11:56 +00:00
for t in target:
2017-10-26 16:37:42 +00:00
SetPESecurityFlagsWorker(t.path)
def SetPESecurityFlagsActionEcho(target, source, env):
for t in target:
if IsPEExecutable(t.path):
print('Setting PE flags on %s' % t.name)
def SetPESecurityFlags(targets):
2017-10-25 23:11:56 +00:00
for t in targets:
2017-10-26 16:37:42 +00:00
a = defenv.Action(SetPESecurityFlagsAction, strfunction=SetPESecurityFlagsActionEcho)
2017-10-25 23:11:56 +00:00
defenv.AddPostAction(t, a)
2020-06-26 22:52:09 +00:00
def MakeReproducible(targets):
for t in targets:
defenv.AddPostAction(t, defenv.Action(MakeReproducibleAction, strfunction=SilentActionEcho))
2008-11-15 21:54:24 +00:00
def TestScript(scripts):
defenv.Install('$TESTDISTDIR/Tests', scripts)
defenv.Distribute = Distribute
defenv.DistributeBin = DistributeBin
defenv.DistributeConf = DistributeConf
defenv.DistributeW32Bin = DistributeW32Bin
defenv.DistributeStubs = DistributeStubs
defenv.DistributePlugin = DistributePlugin
defenv.DistributeContrib = DistributeContrib
defenv.DistributeMenu = DistributeMenu
defenv.DistributeInclude = DistributeInclude
defenv.DistributeDoc = DistributeDoc
defenv.DistributeDocs = DistributeDocs
defenv.DistributeExamples = DistributeExamples
defenv.Sign = Sign
2017-10-26 16:37:42 +00:00
defenv.SetPESecurityFlags = SetPESecurityFlags
2020-06-26 22:52:09 +00:00
defenv.MakeReproducible = MakeReproducible
2008-11-15 21:54:24 +00:00
defenv.TestScript = TestScript
2012-10-13 01:47:50 +00:00
def DistributeExtras(env, target, examples, docs):
if examples:
env.DistributeExamples(examples, path=target)
if docs:
env.DistributeDocs(docs, path=target)
2008-11-15 21:54:24 +00:00
######################################################################
####### Environments ###
######################################################################
if defenv['MSTOOLKIT']:
2015-09-17 14:30:07 +00:00
if GetOptionOrEnv('MSVC_USE_SCRIPT', '!') != '!':
2015-09-18 23:16:34 +00:00
defenv['MSVC_USE_SCRIPT'] = GetOptionOrEnv('MSVC_USE_SCRIPT')
2009-02-04 14:05:48 +00:00
defenv.Tool('mstoolkit', toolpath = [Dir('SCons/Tools').rdir()])
2008-11-15 21:54:24 +00:00
defenv.Append(CCFLAGS = Split('$APPEND_CCFLAGS'))
defenv.Append(LINKFLAGS = Split('$APPEND_LINKFLAGS'))
defenv.Append(CPPPATH = Split('$APPEND_CPPPATH'))
defenv.Append(LIBPATH = Split('$APPEND_LIBPATH'))
defenv.Default('$BUILD_PREFIX')
2010-02-07 21:24:09 +00:00
if 'ZLIB_W32' in defenv:
2010-03-27 15:52:04 +00:00
defenv['ZLIB_W32_INC'] = os.path.dirname(str(
defenv.FindFile('zlib.h',
[
defenv['ZLIB_W32'],
os.path.join(defenv['ZLIB_W32'], 'include')
]
)
))
2015-09-19 18:54:02 +00:00
# Search for import library of zlib for mingw or VisualC
for importlib in ['libzdll.a', 'libz.dll.a', 'zdll.lib']:
2010-03-27 15:52:04 +00:00
defenv['ZLIB_W32_LIB'] = os.path.dirname(str(
defenv.FindFile(importlib,
[
defenv['ZLIB_W32'],
os.path.join(defenv['ZLIB_W32'], 'lib')
]
)
))
if defenv['ZLIB_W32_LIB']:
break
2010-02-07 21:24:09 +00:00
defenv['ZLIB_W32_DLL'] = defenv.FindFile('zlib1.dll',
[defenv['ZLIB_W32'], defenv['ZLIB_W32_LIB']])
2012-08-22 23:32:05 +00:00
defenv['ZLIB_W32_NEW_DLL'] = defenv.FindFile('zlib.dll',
[defenv['ZLIB_W32'], defenv['ZLIB_W32_LIB']])
2010-02-07 21:24:09 +00:00
2008-11-15 21:54:24 +00:00
tools = defenv['TOOLS']
envs = []
if 'msvc' in tools or 'mstoolkit' in tools:
envs = SConscript('SCons/Config/ms')
elif 'gcc' in tools:
envs = SConscript('SCons/Config/gnu')
elif 'hpc++' in tools:
envs = SConscript('SCons/Config/hpc++')
else:
envs = SConscript('SCons/Config/default')
stub_env = envs[0]
makensis_env = envs[1]
plugin_env = envs[2]
util_env = envs[3]
cp_util_env = envs[4]
test_env = envs[5]
2010-06-14 10:07:22 +00:00
stub_uenv = envs[6]
plugin_uenv = envs[7]
2008-11-15 21:54:24 +00:00
2013-03-13 17:23:17 +00:00
Export('plugin_env plugin_uenv')
2008-12-12 16:33:25 +00:00
2008-11-15 21:54:24 +00:00
######################################################################
####### Distribution ###
######################################################################
2019-08-17 19:33:21 +00:00
if defenv['PLATFORM'] == 'win32':
def build_nsis_menu_for_zip(target, source, env):
cmdline = FindMakeNSIS(env, str(env['ZIPDISTDIR']))
cmd = env.Command(None, source, cmdline + ' $SOURCE /X"OutFile %s"' % (target[0].abspath, ))
AlwaysBuild(cmd)
nsis_menu_target = defenv.Command(os.path.join('$ZIPDISTDIR', 'NSIS.exe'),
os.path.join('$ZIPDISTDIR', 'Examples', 'NSISMenu.nsi'),
build_nsis_menu_for_zip)
2020-06-26 22:52:09 +00:00
defenv.MakeReproducible(nsis_menu_target)
2019-08-17 19:33:21 +00:00
defenv.Sign(nsis_menu_target)
2008-11-15 21:54:24 +00:00
dist_zip = 'nsis-${VERSION}${DISTSUFFIX}.zip'
zip_target = defenv.Zip(dist_zip, '$ZIPDISTDIR')
defenv.Alias('dist-zip', zip_target)
AlwaysBuild(defenv.AddPostAction(zip_target, Delete('$ZIPDISTDIR')))
if defenv['PLATFORM'] == 'win32':
optchar = '/'
else:
optchar = '-'
defenv['INSTVER'] = '%sDVERSION=$VERSION' % optchar
2019-10-06 16:15:14 +00:00
if 'VER_MAJOR' in defenv and 'VER_MINOR' in defenv \
and 'VER_REVISION' in defenv and 'VER_BUILD' in defenv:
2008-11-15 21:54:24 +00:00
defenv['INSTVER'] += ' %sDVER_MAJOR=$VER_MAJOR' % optchar
defenv['INSTVER'] += ' %sDVER_MINOR=$VER_MINOR' % optchar
defenv['INSTVER'] += ' %sDVER_REVISION=$VER_REVISION' % optchar
defenv['INSTVER'] += ' %sDVER_BUILD=$VER_BUILD' % optchar
inst_env = {}
inst_env['NSISDIR'] = os.path.abspath(str(defenv['INSTDISTDIR']))
inst_env['NSISCONFDIR'] = os.path.abspath(str(defenv['INSTDISTDIR']))
2010-02-07 21:24:09 +00:00
def build_installer(target, source, env):
cmdline = FindMakeNSIS(env, str(env['INSTDISTDIR'])) + ' %sDOUTFILE=%s %s' % (optchar, target[0].abspath, env['INSTVER'])
2012-08-22 23:32:05 +00:00
if 'ZLIB_W32_NEW_DLL' in env and env['ZLIB_W32_NEW_DLL']:
cmdline += ' %sDUSE_NEW_ZLIB' % optchar
2010-02-07 21:24:09 +00:00
cmd = env.Command(None, source, cmdline + ' $SOURCE')
AlwaysBuild(cmd)
2010-03-26 17:18:17 +00:00
# Comment out the following if you want to see the installation directory
# after the build is finished.
2010-04-12 16:00:17 +00:00
#AlwaysBuild(env.AddPostAction(cmd, Delete('$INSTDISTDIR')))
2010-02-07 21:24:09 +00:00
env.Alias('dist-installer', cmd)
2008-11-15 21:54:24 +00:00
installer_target = defenv.Command('nsis-${VERSION}-setup${DISTSUFFIX}.exe',
2010-02-07 21:24:09 +00:00
os.path.join('$INSTDISTDIR', 'Examples', 'makensis.nsi'),
build_installer,
2008-11-15 21:54:24 +00:00
ENV = inst_env)
defenv.Depends(installer_target, '$INSTDISTDIR')
defenv.Sign(installer_target)
defenv.Alias('dist-installer', installer_target)
defenv.Alias('dist', ['dist-zip', 'dist-installer'])
######################################################################
####### Distribute Basics ###
######################################################################
for d in doc:
if d in defenv['SKIPDOC']:
continue
2010-04-12 16:00:17 +00:00
defenv.DistributeDoc(d)
2008-11-15 21:54:24 +00:00
2010-04-12 16:00:17 +00:00
defenv.DistributeConf('nsisconf.nsh')
2008-11-15 21:54:24 +00:00
######################################################################
####### Stubs ###
######################################################################
2010-06-14 10:07:22 +00:00
def BuildStub(compression, solid, unicode):
2008-11-15 21:54:24 +00:00
suffix = ''
if solid:
suffix = '_solid'
2010-06-14 10:07:22 +00:00
if unicode:
env = stub_uenv.Clone()
else:
env = stub_env.Clone()
2012-10-13 01:47:50 +00:00
suffix = suffix + '-' + GetArcSuffix(env, unicode)
AddEnvStandardFlags(env, entry='NSISWinMainNOCRT')
2008-11-15 21:54:24 +00:00
build_dir = '$BUILD_PREFIX/stub_%s%s' % (compression, suffix)
exports = { 'env' : env, 'compression' : compression, 'solid_compression' : solid }
2010-10-02 16:27:18 +00:00
target = defenv.SConscript(dirs = 'Source/exehead', variant_dir = build_dir, duplicate = False, exports = exports)
2008-11-15 21:54:24 +00:00
env.SideEffect('%s/stub_%s.map' % (build_dir, stub), target)
2020-06-26 22:52:09 +00:00
env.MakeReproducible(target)
2008-11-15 21:54:24 +00:00
env.DistributeStubs(target, names=compression+suffix)
defenv.Alias(compression, target)
defenv.Alias('stubs', target)
for stub in stubs:
if stub in defenv['SKIPSTUBS']:
continue
2010-06-14 10:07:22 +00:00
if defenv['UNICODE']:
BuildStub(stub, False, True)
BuildStub(stub, True, True)
2012-10-13 01:47:50 +00:00
if GetArcCPU(defenv)=='x86':
BuildStub(stub, False, False)
BuildStub(stub, True, False)
2013-09-06 23:48:59 +00:00
# BUGBUG64: Should build x86 stubs on x64?
2008-11-15 21:54:24 +00:00
defenv.DistributeStubs('Source/exehead/uninst.ico',names='uninst')
######################################################################
####### makensis ###
######################################################################
build_dir = '$BUILD_PREFIX/makensis'
exports = { 'env' : makensis_env }
2010-10-02 16:27:18 +00:00
makensis = defenv.SConscript(dirs = 'Source', variant_dir = build_dir, duplicate = False, exports = exports)
2008-11-15 21:54:24 +00:00
makensis_env.SideEffect('%s/makensis.map' % build_dir, makensis)
2020-06-26 22:52:09 +00:00
defenv.MakeReproducible(makensis)
2008-11-15 21:54:24 +00:00
defenv.Alias('makensis', makensis)
2010-02-07 21:24:09 +00:00
if defenv['PLATFORM'] == 'win32':
2010-04-17 23:20:18 +00:00
defenv.DistributeW32Bin(makensis, alias='install-compiler')
2010-02-07 21:24:09 +00:00
else:
2010-04-17 23:20:18 +00:00
defenv.DistributeBin(makensis, alias='install-compiler')
2008-11-15 21:54:24 +00:00
######################################################################
####### Plug-ins ###
######################################################################
2012-10-13 01:47:50 +00:00
def PerformPluginExtrasDistOperationOnce(env, unicode):
#SCons does not like it if you install the same file multiple times
return GetArcCPU(defenv)==GetArcCPU(env) and (defenv['UNICODE']==unicode)
2012-03-05 23:29:20 +00:00
def BuildPluginWorker(target, source, libs, examples = None, docs = None,
2010-06-14 10:07:22 +00:00
entry = 'DllMain', res = None, resources = None,
defines = None, flags = None, nodeflib = True,
cppused = False, unicode = False):
2012-10-13 01:47:50 +00:00
basename = target
2010-06-14 10:07:22 +00:00
if unicode:
env = plugin_uenv.Clone()
else:
env = plugin_env.Clone()
2008-11-15 21:54:24 +00:00
if cppused and env['CPP_REQUIRES_STDLIB']:
nodeflib = False
2008-12-12 16:33:25 +00:00
AddEnvStandardFlags(env, defines, flags, libs, entry, nodeflib)
2008-11-15 21:54:24 +00:00
AppendRES(env, source, res, resources)
2008-12-12 16:33:25 +00:00
plugin = env.SharedLibrary(target, source)
2008-11-15 21:54:24 +00:00
defenv.Alias(target, plugin)
defenv.Alias('plugins', plugin)
2017-10-26 16:37:42 +00:00
defenv.SetPESecurityFlags(plugin)
2020-06-26 22:52:09 +00:00
defenv.MakeReproducible(plugin)
2008-11-15 21:54:24 +00:00
defenv.Sign(plugin)
CleanMap(env, plugin, target)
for i in plugin:
if str(i)[-4:].lower() == '.dll':
plugin = i
break
2012-10-13 01:47:50 +00:00
env.DistributePlugin(plugin, arcsubpath = GetArcSuffix(env, unicode))
2012-03-05 23:29:20 +00:00
2012-10-13 01:47:50 +00:00
if PerformPluginExtrasDistOperationOnce(env, unicode): # only distribute extras once
DistributeExtras(env, basename, examples, docs)
2012-03-05 23:29:20 +00:00
def BuildPlugin(target, source, libs, examples = None, docs = None,
entry = 'DllMain', res = None, resources = None,
defines = None, flags = None, nodeflib = True,
cppused = False):
unicodetarget = 'UNICODE' in exports['env']['CPPDEFINES']
BuildPluginWorker(target, source, libs, examples, docs, entry, res, resources, defines, flags, nodeflib, cppused, unicodetarget)
2008-12-12 16:33:25 +00:00
for plugin in plugin_libs + plugins:
2008-11-15 21:54:24 +00:00
if plugin in defenv['SKIPPLUGINS']:
continue
2012-03-05 23:29:20 +00:00
srcpath = 'Contrib/' + plugin
2008-11-15 21:54:24 +00:00
build_dir = '$BUILD_PREFIX/' + plugin
2014-02-08 00:13:52 +00:00
pvariants = []
if GetArcCPU(defenv) == 'x86':
pvariants += [{'e':plugin_env.Clone()}]
2010-06-14 10:07:22 +00:00
if defenv['UNICODE']:
2012-10-13 01:47:50 +00:00
pvariants += [{'e':plugin_uenv.Clone()}]
2012-03-05 23:29:20 +00:00
for pvariant in pvariants:
2012-10-13 01:47:50 +00:00
exports = {
'env' : pvariant['e'],
'BuildPlugin' : BuildPlugin, 'GetArcSuffix' : GetArcSuffix,
'PerformPluginExtrasDistOperationOnce' : PerformPluginExtrasDistOperationOnce
}
vdir = build_dir + '/' + GetArcSuffix(pvariant['e'])
2012-03-05 23:29:20 +00:00
defenv.SConscript(dirs = srcpath, variant_dir = vdir, duplicate = False, exports = exports)
2008-11-15 21:54:24 +00:00
######################################################################
####### Utilities ###
######################################################################
2010-02-07 21:24:09 +00:00
Import('AddZLib')
2008-12-12 16:33:25 +00:00
def BuildUtilEnv(defines = None, flags = None, libs = None,
entry = None, nodeflib = None,
2011-11-19 19:53:59 +00:00
cross_platform = False, cli = False):
2008-11-15 21:54:24 +00:00
if not cross_platform:
env = util_env.Clone()
2010-02-07 21:24:09 +00:00
platform = 'win32'
2008-11-15 21:54:24 +00:00
else:
env = cp_util_env.Clone()
2010-02-07 21:24:09 +00:00
platform = env['PLATFORM']
2011-11-19 19:53:59 +00:00
2010-02-07 21:24:09 +00:00
if libs and 'z' in libs:
libs.remove('z')
AddZLib(env, platform)
2008-11-15 21:54:24 +00:00
2014-01-21 14:13:00 +00:00
if platform == 'win32':
if cli:
env.Append(LINKFLAGS = env['SUBSYS_CON'])
else:
env.Append(LINKFLAGS = env['SUBSYS_WIN'])
2012-10-13 01:47:50 +00:00
2008-12-12 16:33:25 +00:00
AddEnvStandardFlags(env, defines, flags, libs, entry, nodeflib)
2008-11-15 21:54:24 +00:00
return env
def BuildUtil(target, source, libs, entry = None, res = None,
resources = None, defines = None, flags = None,
nodeflib = False, file_name = '', path='', contrib = False,
examples = None, docs = None, cross_platform = False,
2011-11-19 20:22:56 +00:00
root_util = False, cli = False, noinstall = False):
2011-11-19 19:53:59 +00:00
env = BuildUtilEnv(defines, flags, libs, entry, nodeflib, cross_platform, cli)
2008-11-15 21:54:24 +00:00
AppendRES(env, source, res, resources)
if file_name != '':
target = "%s/%s" % (target, file_name)
# make sure the environment suffix fits
if env['PROGSUFFIX'] not in target:
if '.' in target:
env['PROGSUFFIX'] = target[target.rindex('.'):]
2008-12-12 16:33:25 +00:00
util = env.Program(target, source)
2008-11-15 21:54:24 +00:00
defenv.Alias(target, util)
defenv.Alias('utils', util)
2020-06-26 22:52:09 +00:00
defenv.MakeReproducible(util)
2008-11-15 21:54:24 +00:00
defenv.Sign(util)
CleanMap(env, util, target)
2011-11-19 20:22:56 +00:00
if not noinstall:
if contrib:
ins = env.DistributeContrib(util, path=path, alias='install-utils')
elif cross_platform and not env['PLATFORM'] == 'win32' or root_util and env['PLATFORM'] == 'win32':
ins = env.DistributeBin(util, path=path, alias='install-utils')
else:
ins = env.DistributeW32Bin(util, path=path, alias='install-utils')
DistributeExtras(env, target, examples, docs)
2008-11-15 21:54:24 +00:00
return util
for util in utils:
if util in defenv['SKIPUTILS']:
continue
path = 'Contrib/' + util
build_dir = '$BUILD_PREFIX/' + util
2017-05-14 16:16:30 +00:00
exports = {'BuildUtil' : BuildUtil, 'BuildUtilEnv' : BuildUtilEnv, 'env' : util_env, 'GetArcCPU' : GetArcCPU}
2008-11-15 21:54:24 +00:00
2010-10-02 16:27:18 +00:00
defenv.SConscript(dirs = path, variant_dir = build_dir, duplicate = False, exports = exports)
2008-11-15 21:54:24 +00:00
######################################################################
####### Documentation ###
######################################################################
halibut = defenv.SConscript(
dirs = 'Docs/src/bin/halibut',
2010-10-02 16:27:18 +00:00
variant_dir = '$BUILD_PREFIX/halibut',
2008-11-15 21:54:24 +00:00
duplicate = False,
exports = {'env' : defenv.Clone()}
)
2011-12-02 23:18:34 +00:00
for doctype in defenv['DOCTYPES']:
2008-11-15 21:54:24 +00:00
defenv.SConscript(
dirs = 'Docs/src',
2011-12-02 23:18:34 +00:00
variant_dir = '$BUILD_PREFIX/Docs/' + doctype,
2008-11-15 21:54:24 +00:00
duplicate = False,
2011-12-02 23:18:34 +00:00
exports = {'halibut' : halibut, 'env' : defenv.Clone(), 'build_doctype' : doctype}
2008-11-15 21:54:24 +00:00
)
######################################################################
####### Examples ###
######################################################################
defenv.SConscript(
dirs = 'Examples',
exports = {'env': defenv.Clone()}
)
######################################################################
####### Includes ###
######################################################################
defenv.SConscript(
dirs = 'Include',
exports = {'env': defenv.Clone()}
)
######################################################################
####### Miscellaneous ###
######################################################################
for i in misc:
if i in defenv['SKIPMISC']:
continue
defenv.SConscript(dirs = 'Contrib/%s' % i)
######################################################################
####### Tests ###
######################################################################
# test code
build_dir = '$BUILD_PREFIX/tests'
exports = {'env' : test_env.Clone()}
defenv.SConscript(
dirs = 'Source/Tests',
duplicate = False,
exports = exports,
2010-10-02 16:27:18 +00:00
variant_dir = build_dir
2008-11-15 21:54:24 +00:00
)
defenv.Ignore('$BUILD_PREFIX', '$BUILD_PREFIX/tests')
# test scripts
test_scripts_env = defenv.Clone(ENV = os.environ) # env needed for some scripts
test_scripts_env['ENV']['NSISDIR'] = os.path.abspath(str(defenv['TESTDISTDIR']))
test_scripts_env['ENV']['NSISCONFDIR'] = os.path.abspath(str(defenv['TESTDISTDIR']))
2009-02-05 07:26:16 +00:00
test_scripts_env.PrependENVPath('PATH', os.path.abspath(str(defenv['TESTDISTDIR'])))
2008-11-15 21:54:24 +00:00
def test_scripts(target, source, env):
from os import walk, sep
instdir = source[0].path
tdlen = len(env.subst('$TESTDISTDIR'))
skipped_tests = env['SKIPTESTS'].split(',')
ignored_tests = env['IGNORETESTS'].split(',')
2020-08-29 19:30:27 +00:00
skipped_tests += ['Examples' + sep + 'AppGen.nsi'] # Never test this, not a real installer
2008-11-15 21:54:24 +00:00
2010-02-07 21:24:09 +00:00
compiler = FindMakeNSIS(env, env.subst('$TESTDISTDIR'))
2008-11-15 21:54:24 +00:00
for root, dirs, files in walk(instdir):
for file in files:
if file[-4:] == '.nsi':
nsi = root + sep + file
nsif = nsi[tdlen + 1:]
if nsif in skipped_tests:
continue
if nsif in ignored_tests:
2010-02-07 21:24:09 +00:00
cmd = env.Command(None, nsi, '-' + compiler + ' $SOURCE')
2008-11-15 21:54:24 +00:00
else:
2010-02-07 21:24:09 +00:00
cmd = env.Command(None, nsi, compiler + ' $SOURCE')
2008-11-15 21:54:24 +00:00
AlwaysBuild(cmd)
env.Alias('test-scripts', cmd)
return None
test = test_scripts_env.Command('test-scripts.log', '$TESTDISTDIR', test_scripts)
test_scripts_env.Alias('test-scripts', test)
# test all
defenv.Alias('test', ['test-code', 'test-scripts'])