Bug 1429875 - Remove expandlibs and instead generate list files in the mozbuild backend. r=glandium

MozReview-Commit-ID: 5eLwnh1HHGj

--HG--
extra : rebase_source : cd308adc4542be0f9b33b64d31a2d0dc0310fcd4
This commit is contained in:
Chris Manchester 2018-03-20 16:31:05 -07:00
Родитель 47f253d6f9
Коммит de12a7992b
17 изменённых файлов: 209 добавлений и 1104 удалений

Просмотреть файл

@ -13,10 +13,6 @@ endif
ifeq ($(HOST_OS_ARCH)_$(OS_ARCH),Linux_Darwin)
# Use the host compiler instead of the target compiler.
CXX := $(HOST_CXX)
# expandlibs doesn't know the distinction between host and target toolchains,
# and on cross linux/darwin builds, the options to give to the linker for file
# lists differ between both, so don't use file lists.
EXPAND_MKSHLIB_ARGS :=
endif
# Use the default OS X deployment target to enable using the libc++ headers

Просмотреть файл

@ -418,18 +418,6 @@ CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(MOZILLA_DIR)/config/createprecomp
# MDDEPDIR is the subdirectory where dependency files are stored
MDDEPDIR := .deps
EXPAND_LIBS_EXEC = $(PYTHON) $(MOZILLA_DIR)/config/expandlibs_exec.py
EXPAND_LIBS_GEN = $(PYTHON) $(MOZILLA_DIR)/config/expandlibs_gen.py
EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
EXPAND_LINK = $(EXPAND_LIBS_EXEC) --uselist -- $(LINKER)
EXPAND_MKSHLIB_ARGS = --uselist
ifdef SYMBOL_ORDER
EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
endif
EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
# $(call CHECK_SYMBOLS,lib,PREFIX,dep_name,test)
# Checks that the given `lib` doesn't contain dependency on symbols with a
# version starting with `PREFIX`_ and matching the `test`. `dep_name` is only

Просмотреть файл

@ -1,143 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
'''Expandlibs is a system that allows to replace some libraries with a
descriptor file containing some linking information about them.
The descriptor file format is as follows:
---8<-----
OBJS = a.o b.o ...
LIBS = libfoo.a libbar.a ...
--->8-----
(In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a).
Expandlibs also canonicalizes how to pass libraries to the linker, such
that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used:
given a list of files, expandlibs will replace items with the form
${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules:
- If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or
${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead
- If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it
- If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists,
replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
descriptor contains. And for each of these LIBS, also apply the same
rules.
'''
from __future__ import with_statement
import sys, os, errno
import expandlibs_config as conf
def ensureParentDir(file):
'''Ensures the directory parent to the given file exists'''
dir = os.path.dirname(file)
if dir and not os.path.exists(dir):
try:
os.makedirs(dir)
except OSError, error:
if error.errno != errno.EEXIST:
raise
def relativize(path):
'''Returns a path relative to the current working directory, if it is
shorter than the given path'''
def splitpath(path):
dir, file = os.path.split(path)
if os.path.splitdrive(dir)[1] == os.sep:
return [file]
return splitpath(dir) + [file]
if not os.path.exists(path):
return path
curdir = splitpath(os.path.abspath(os.curdir))
abspath = splitpath(os.path.abspath(path))
while curdir and abspath and curdir[0] == abspath[0]:
del curdir[0]
del abspath[0]
if not curdir and not abspath:
return '.'
relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
if len(path) > len(relpath):
return relpath
return path
def isObject(path):
'''Returns whether the given path points to an object file, that is,
ends with OBJ_SUFFIX or .i_o'''
return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o']
def isDynamicLib(path):
'''Returns whether the given path points to a dynamic library, that is,
ends with DLL_SUFFIX.'''
# On mac, the xul library is named XUL, instead of libxul.dylib. Assume any
# file by that name is a dynamic library.
return os.path.splitext(path)[1] == conf.DLL_SUFFIX or os.path.basename(path) == 'XUL'
class LibDescriptor(dict):
KEYS = ['OBJS', 'LIBS']
def __init__(self, content=None):
'''Creates an instance of a lib descriptor, initialized with contents
from a list of strings when given. This is intended for use with
file.readlines()'''
if isinstance(content, list) and all([isinstance(item, str) for item in content]):
pass
elif content is not None:
raise TypeError("LibDescriptor() arg 1 must be None or a list of strings")
super(LibDescriptor, self).__init__()
for key in self.KEYS:
self[key] = []
if not content:
return
for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]:
if key in self.KEYS:
self[key] = value.split()
def __str__(self):
'''Serializes the lib descriptor'''
return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k]))
class ExpandArgs(list):
def __init__(self, args):
'''Creates a clone of the |args| list and performs file expansion on
each item it contains'''
super(ExpandArgs, self).__init__()
self._descs = set()
for arg in args:
self += self._expand(arg)
def _expand(self, arg):
'''Internal function doing the actual work'''
(root, ext) = os.path.splitext(arg)
if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
return [relativize(arg)]
if conf.LIB_PREFIX:
dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
else:
dll = root + conf.DLL_SUFFIX
if os.path.exists(dll):
if conf.IMPORT_LIB_SUFFIX:
return [relativize(root + conf.IMPORT_LIB_SUFFIX)]
else:
return [relativize(dll)]
return self._expand_desc(arg)
def _expand_desc(self, arg):
'''Internal function taking care of lib descriptor expansion only'''
desc = os.path.abspath(arg + conf.LIBS_DESC_SUFFIX)
if os.path.exists(desc):
if desc in self._descs:
return []
self._descs.add(desc)
with open(desc, 'r') as f:
desc = LibDescriptor(f.readlines())
objs = [relativize(o) for o in desc['OBJS']]
for lib in desc['LIBS']:
objs += self._expand(lib)
return objs
return [relativize(arg)]
if __name__ == '__main__':
print " ".join(ExpandArgs(sys.argv[1:]))

Просмотреть файл

@ -1,29 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from buildconfig import substs
def normalize_suffix(suffix):
'''Returns a normalized suffix, i.e. ensures it starts with a dot and
doesn't starts or ends with whitespace characters'''
value = suffix.strip()
if len(value) and not value.startswith('.'):
value = '.' + value
return value
# Variables from the build system
AR = substs['AR']
AR_EXTRACT = substs['AR_EXTRACT'].replace('$(AR)', AR)
DLL_PREFIX = substs['DLL_PREFIX']
LIB_PREFIX = substs['LIB_PREFIX']
RUST_LIB_PREFIX = substs['RUST_LIB_PREFIX']
OBJ_SUFFIX = normalize_suffix(substs['OBJ_SUFFIX'])
LIB_SUFFIX = normalize_suffix(substs['LIB_SUFFIX'])
RUST_LIB_SUFFIX = normalize_suffix(substs['RUST_LIB_SUFFIX'])
DLL_SUFFIX = normalize_suffix(substs['DLL_SUFFIX'])
IMPORT_LIB_SUFFIX = normalize_suffix(substs['IMPORT_LIB_SUFFIX'])
LIBS_DESC_SUFFIX = normalize_suffix(substs['LIBS_DESC_SUFFIX'])
EXPAND_LIBS_LIST_STYLE = substs['EXPAND_LIBS_LIST_STYLE']
EXPAND_LIBS_ORDER_STYLE = substs['EXPAND_LIBS_ORDER_STYLE']
LD_PRINT_ICF_SECTIONS = substs['LD_PRINT_ICF_SECTIONS']

Просмотреть файл

@ -1,354 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
'''expandlibs-exec.py applies expandlibs rules, and some more (see below) to
a given command line, and executes that command line with the expanded
arguments.
With the --extract argument (useful for e.g. $(AR)), it extracts object files
from static libraries (or use those listed in library descriptors directly).
With the --uselist argument (useful for e.g. $(CC)), it replaces all object
files with a list file. This can be used to avoid limitations in the length
of a command line. The kind of list file format used depends on the
EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
or 'linkerscript' for GNU ld linker scripts.
See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
With the --symbol-order argument, followed by a file name, it will add the
relevant linker options to change the order in which the linker puts the
symbols appear in the resulting binary. Only works for ELF targets.
'''
from __future__ import with_statement
import sys
import os
from expandlibs import (
ExpandArgs,
relativize,
isDynamicLib,
isObject,
)
import expandlibs_config as conf
from optparse import OptionParser
import subprocess
import tempfile
import shutil
import subprocess
import re
from mozbuild.makeutil import Makefile
# The are the insert points for a GNU ld linker script, assuming a more
# or less "standard" default linker script. This is not a dict because
# order is important.
SECTION_INSERT_BEFORE = [
('.text', '.fini'),
('.rodata', '.rodata1'),
('.data.rel.ro', '.dynamic'),
('.data', '.data1'),
]
class ExpandArgsMore(ExpandArgs):
''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
def __enter__(self):
self.tmp = []
return self
def __exit__(self, type, value, tb):
'''Automatically remove temporary files'''
for tmp in self.tmp:
if os.path.isdir(tmp):
shutil.rmtree(tmp, True)
else:
os.remove(tmp)
def extract(self):
self[0:] = self._extract(self)
def _extract(self, args):
'''When a static library name is found, either extract its contents
in a temporary directory or use the information found in the
corresponding lib descriptor.
'''
ar_extract = conf.AR_EXTRACT.split()
newlist = []
def lookup(base, f):
for root, dirs, files in os.walk(base):
if f in files:
return os.path.join(root, f)
for arg in args:
if os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
newlist += self._extract(self._expand_desc(arg))
continue
elif os.path.exists(arg) and (len(ar_extract) or conf.AR == 'lib'):
tmp = tempfile.mkdtemp(dir=os.curdir)
self.tmp.append(tmp)
if conf.AR == 'lib':
out = subprocess.check_output([conf.AR, '-NOLOGO', '-LIST', arg])
files = out.splitlines()
# If lib -list returns a list full of dlls, it's an
# import lib.
if all(isDynamicLib(f) for f in files):
newlist += [arg]
continue
for f in files:
subprocess.call([conf.AR, '-NOLOGO', '-EXTRACT:%s' % f, os.path.abspath(arg)], cwd=tmp)
else:
subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp)
objs = []
basedir = os.path.dirname(arg)
for root, dirs, files in os.walk(tmp):
for f in files:
if isObject(f):
# If the file extracted from the library also
# exists in the directory containing the
# library, or one of its subdirectories, use
# that instead.
maybe_obj = lookup(os.path.join(basedir, os.path.relpath(root, tmp)), f)
if maybe_obj:
objs.append(relativize(maybe_obj))
else:
objs.append(relativize(os.path.join(root, f)))
newlist += sorted(objs)
continue
newlist += [arg]
return newlist
def makelist(self):
'''Replaces object file names with a temporary list file, using a
list format depending on the EXPAND_LIBS_LIST_STYLE variable
'''
objs = [o for o in self if isObject(o)]
if not len(objs): return
fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
content = ['INPUT("%s")\n' % obj for obj in objs]
ref = tmp
elif conf.EXPAND_LIBS_LIST_STYLE == "filelist":
content = ["%s\n" % obj for obj in objs]
ref = "-Wl,-filelist," + tmp
elif conf.EXPAND_LIBS_LIST_STYLE == "list":
content = ["%s\n" % obj for obj in objs]
ref = "@" + tmp
else:
os.close(fd)
os.remove(tmp)
return
self.tmp.append(tmp)
f = os.fdopen(fd, "w")
f.writelines(content)
f.close()
idx = self.index(objs[0])
newlist = self[0:idx] + [ref] + [os.path.normpath(item) for item in self[idx:] if item not in objs]
self[0:] = newlist
def _getFoldedSections(self):
'''Returns a dict about folded sections.
When section A and B are folded into section C, the dict contains:
{ 'A': 'C',
'B': 'C',
'C': ['A', 'B'] }'''
if not conf.LD_PRINT_ICF_SECTIONS:
return {}
proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
(stdout, stderr) = proc.communicate()
result = {}
# gold's --print-icf-sections output looks like the following:
# ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o'
# In terms of words, chances are this will change in the future,
# especially considering "into" is misplaced. Splitting on quotes
# seems safer.
for l in stderr.split('\n'):
quoted = l.split("'")
if len(quoted) > 5 and quoted[1] != quoted[5]:
result[quoted[1]] = [quoted[5]]
if quoted[5] in result:
result[quoted[5]].append(quoted[1])
else:
result[quoted[5]] = [quoted[1]]
return result
def _getOrderedSections(self, ordered_symbols):
'''Given an ordered list of symbols, returns the corresponding list
of sections following the order.'''
if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX])
folded = self._getFoldedSections()
sections = set()
ordered_sections = []
for symbol in ordered_symbols:
symbol_sections = finder.getSections(symbol)
all_symbol_sections = []
for section in symbol_sections:
if section in folded:
if isinstance(folded[section], str):
section = folded[section]
all_symbol_sections.append(section)
all_symbol_sections.extend(folded[section])
else:
all_symbol_sections.append(section)
for section in all_symbol_sections:
if not section in sections:
ordered_sections.append(section)
sections.add(section)
return ordered_sections
def orderSymbols(self, order):
'''Given a file containing a list of symbols, adds the appropriate
argument to make the linker put the symbols in that order.'''
with open(order) as file:
sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()])
split_sections = {}
linked_sections = [s[0] for s in SECTION_INSERT_BEFORE]
for s in sections:
for linked_section in linked_sections:
if s.startswith(linked_section):
if linked_section in split_sections:
split_sections[linked_section].append(s)
else:
split_sections[linked_section] = [s]
break
content = []
# Order is important
linked_sections = [s for s in linked_sections if s in split_sections]
if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file':
option = '-Wl,--section-ordering-file,%s'
content = sections
for linked_section in linked_sections:
content.extend(split_sections[linked_section])
content.append('%s.*' % linked_section)
content.append(linked_section)
elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript':
option = '-Wl,-T,%s'
section_insert_before = dict(SECTION_INSERT_BEFORE)
for linked_section in linked_sections:
content.append('SECTIONS {')
content.append(' %s : {' % linked_section)
content.extend(' *(%s)' % s for s in split_sections[linked_section])
content.append(' }')
content.append('}')
content.append('INSERT BEFORE %s' % section_insert_before[linked_section])
else:
raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
fd, tmp = tempfile.mkstemp(dir=os.curdir)
f = os.fdopen(fd, "w")
f.write('\n'.join(content)+'\n')
f.close()
self.tmp.append(tmp)
self.append(option % tmp)
class SectionFinder(object):
'''Instances of this class allow to map symbol names to sections in
object files.'''
def __init__(self, objs):
'''Creates an instance, given a list of object files.'''
if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
self.mapping = {}
for obj in objs:
if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX:
raise Exception('%s is not an object nor a static library' % obj)
for symbol, section in SectionFinder._getSymbols(obj):
sym = SectionFinder._normalize(symbol)
if sym in self.mapping:
if not section in self.mapping[sym]:
self.mapping[sym].append(section)
else:
self.mapping[sym] = [section]
def getSections(self, symbol):
'''Given a symbol, returns a list of sections containing it or the
corresponding thunks. When the given symbol is a thunk, returns the
list of sections containing its corresponding normal symbol and the
other thunks for that symbol.'''
sym = SectionFinder._normalize(symbol)
if sym in self.mapping:
return self.mapping[sym]
return []
@staticmethod
def _normalize(symbol):
'''For normal symbols, return the given symbol. For thunks, return
the corresponding normal symbol.'''
if re.match('^_ZThn[0-9]+_', symbol):
return re.sub('^_ZThn[0-9]+_', '_Z', symbol)
return symbol
@staticmethod
def _getSymbols(obj):
'''Returns a list of (symbol, section) contained in the given object
file.'''
proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
(stdout, stderr) = proc.communicate()
syms = []
for line in stdout.splitlines():
# Each line has the following format:
# <addr> [lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>
tmp = line.split(' ',1)
# This gives us ["<addr>", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>"]
# We only need to consider cases where "<section>\t<length> <symbol>" is present,
# and where the [FfO] flag is either F (function) or O (object).
if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']:
tmp = tmp[1][8:].split()
# That gives us ["<section>","<length>", "<symbol>"]
syms.append((tmp[-1], tmp[0]))
return syms
def print_command(out, args):
print >>out, "Executing: " + " ".join(args)
for tmp in [f for f in args.tmp if os.path.isfile(f)]:
print >>out, tmp + ":"
with open(tmp) as file:
print >>out, "".join([" " + l for l in file.readlines()])
out.flush()
def main(args, proc_callback=None):
parser = OptionParser()
parser.add_option("--extract", action="store_true", dest="extract",
help="when a library has no descriptor file, extract it first, when possible")
parser.add_option("--uselist", action="store_true", dest="uselist",
help="use a list file for objects when executing a command")
parser.add_option("--verbose", action="store_true", dest="verbose",
help="display executed command and temporary files content")
parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE",
help="use the given list of symbols to order symbols in the resulting binary when using with a linker")
(options, args) = parser.parse_args(args)
with ExpandArgsMore(args) as args:
if options.extract:
args.extract()
if options.symbol_order:
args.orderSymbols(options.symbol_order)
if options.uselist:
args.makelist()
if options.verbose:
print_command(sys.stderr, args)
try:
proc = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
if proc_callback:
proc_callback(proc)
except Exception, e:
print >>sys.stderr, 'error: Launching', args, ':', e
raise e
(stdout, stderr) = proc.communicate()
if proc.returncode and not options.verbose:
print_command(sys.stderr, args)
sys.stderr.write(stdout)
sys.stderr.flush()
if proc.returncode:
return proc.returncode
return 0
if __name__ == '__main__':
exit(main(sys.argv[1:]))

Просмотреть файл

@ -1,41 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
'''Given a list of object files and library names, prints a library
descriptor to standard output'''
from __future__ import with_statement
import sys
import os
import expandlibs_config as conf
from expandlibs import LibDescriptor, isObject, ensureParentDir
from optparse import OptionParser
def generate(args):
desc = LibDescriptor()
for arg in args:
if isObject(arg):
if os.path.exists(arg):
desc['OBJS'].append(os.path.abspath(arg))
else:
raise Exception("File not found: %s" % arg)
elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
if os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
desc['LIBS'].append(os.path.abspath(arg))
else:
raise Exception("File not found: %s" % arg)
return desc
if __name__ == '__main__':
parser = OptionParser()
parser.add_option("-o", dest="output", metavar="FILE",
help="send output to the given file")
(options, args) = parser.parse_args()
if not options.output:
raise Exception("Missing option: -o")
ensureParentDir(options.output)
with open(options.output, 'w') as outfile:
print >>outfile, generate(args)

Просмотреть файл

@ -101,9 +101,8 @@ endif # ENABLE_TESTS
ifndef LIBRARY
ifdef REAL_LIBRARY
ifdef NO_EXPAND_LIBS
# Only build actual library if it is requested.
LIBRARY := $(REAL_LIBRARY)
else
LIBRARY := $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX)
endif
endif
endif
@ -431,9 +430,6 @@ everything::
$(MAKE) clean
$(MAKE) all
STATIC_LIB_DEP = $(if $(wildcard $(1).$(LIBS_DESC_SUFFIX)),$(1).$(LIBS_DESC_SUFFIX),$(1))
STATIC_LIBS_DEPS := $(foreach l,$(STATIC_LIBS),$(call STATIC_LIB_DEP,$(l)))
# Dependencies which, if modified, should cause everything to rebuild
GLOBAL_DEPS += Makefile $(addprefix $(DEPTH)/config/,$(INCLUDED_AUTOCONF_MK)) $(MOZILLA_DIR)/config/config.mk
@ -447,6 +443,12 @@ host:: $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(HOST_RUST_PROGR
target:: $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(RUST_LIBRARY_FILE) $(RUST_PROGRAMS)
ifndef LIBRARY
ifdef OBJS
target:: $(OBJS)
endif
endif
syms::
include $(MOZILLA_DIR)/config/makefiles/target_binaries.mk
@ -530,18 +532,18 @@ alltags:
find $(topsrcdir) -name dist -prune -o \( -name '*.[hc]' -o -name '*.cp' -o -name '*.cpp' -o -name '*.idl' \) -print | $(TAG_PROGRAM)
define EXPAND_CC_OR_CXX
$(if $(PROG_IS_C_ONLY_$(1)),$(EXPAND_CC),$(EXPAND_CCC))
$(if $(PROG_IS_C_ONLY_$(1)),$(CC),$(CCC))
endef
#
# PROGRAM = Foo
# creates OBJS, links with LIBS to create Foo
#
$(PROGRAM): $(PROGOBJS) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(RESFILE) $(GLOBAL_DEPS) $(call mkdir_deps,$(FINAL_TARGET))
$(PROGRAM): $(PROGOBJS) $(STATIC_LIBS) $(EXTRA_DEPS) $(RESFILE) $(GLOBAL_DEPS) $(call mkdir_deps,$(FINAL_TARGET))
$(REPORT_BUILD)
@$(RM) $@.manifest
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
$(EXPAND_LINK) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) -IMPLIB:$(basename $(@F)).lib $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(STATIC_LIBS) $(SHARED_LIBS) $(OS_LIBS)
$(LINKER) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) -IMPLIB:$(basename $(@F)).lib $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $($(notdir $@)_OBJS) $(RESFILE) $(STATIC_LIBS) $(SHARED_LIBS) $(OS_LIBS)
ifdef MSMANIFEST_TOOL
@if test -f $@.manifest; then \
if test -f '$(srcdir)/$(notdir $@).manifest'; then \
@ -562,7 +564,7 @@ ifdef MOZ_PROFILE_GENERATE
touch -t `date +%Y%m%d%H%M.%S -d 'now+5seconds'` pgo.relink
endif
else # !WINNT || GNU_CC
$(call EXPAND_CC_OR_CXX,$@) -o $@ $(COMPUTED_CXX_LDFLAGS) $(PGO_CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(OS_LIBS)
$(call EXPAND_CC_OR_CXX,$@) -o $@ $(COMPUTED_CXX_LDFLAGS) $(PGO_CFLAGS) $($(notdir $@)_OBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(OS_LIBS)
$(call CHECK_BINARY,$@)
endif # WINNT && !GNU_CC
@ -576,7 +578,7 @@ endif
$(HOST_PROGRAM): $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_DEPS) $(GLOBAL_DEPS)
$(REPORT_BUILD)
ifeq (_WINNT,$(GNU_CC)_$(HOST_OS_ARCH))
$(EXPAND_LIBS_EXEC) -- $(LINKER) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $(HOST_OBJS) $(WIN32_EXE_LDFLAGS) $(HOST_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
$(LINKER) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $(HOST_OBJS) $(WIN32_EXE_LDFLAGS) $(HOST_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
ifdef MSMANIFEST_TOOL
@if test -f $@.manifest; then \
if test -f '$(srcdir)/$@.manifest'; then \
@ -593,9 +595,9 @@ ifdef MSMANIFEST_TOOL
endif # MSVC with manifest tool
else
ifeq ($(HOST_CPP_PROG_LINK),1)
$(EXPAND_LIBS_EXEC) -- $(HOST_CXX) -o $@ $(HOST_CXX_LDFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
$(HOST_CXX) -o $@ $(HOST_CXX_LDFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
else
$(EXPAND_LIBS_EXEC) -- $(HOST_CC) -o $@ $(HOST_C_LDFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
$(HOST_CC) -o $@ $(HOST_C_LDFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
endif # HOST_CPP_PROG_LINK
endif
ifndef CROSS_COMPILE
@ -610,10 +612,10 @@ endif
# SIMPLE_PROGRAMS = Foo Bar
# creates Foo.o Bar.o, links with LIBS to create Foo, Bar.
#
$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(STATIC_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(REPORT_BUILD)
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
$(EXPAND_LINK) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(OS_LIBS)
$(LINKER) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $($@_OBJS) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(OS_LIBS)
ifdef MSMANIFEST_TOOL
@if test -f $@.manifest; then \
$(MT) -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
@ -621,7 +623,7 @@ ifdef MSMANIFEST_TOOL
fi
endif # MSVC with manifest tool
else
$(call EXPAND_CC_OR_CXX,$@) $(COMPUTED_CXX_LDFLAGS) $(PGO_CFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(OS_LIBS)
$(call EXPAND_CC_OR_CXX,$@) $(COMPUTED_CXX_LDFLAGS) $(PGO_CFLAGS) -o $@ $($@_OBJS) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(OS_LIBS)
$(call CHECK_BINARY,$@)
endif # WINNT && !GNU_CC
@ -635,29 +637,22 @@ endif
$(HOST_SIMPLE_PROGRAMS): host_%$(HOST_BIN_SUFFIX): host_%.$(OBJ_SUFFIX) $(HOST_LIBS) $(HOST_EXTRA_DEPS) $(GLOBAL_DEPS)
$(REPORT_BUILD)
ifeq (WINNT_,$(HOST_OS_ARCH)_$(GNU_CC))
$(EXPAND_LIBS_EXEC) -- $(LINKER) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
$(LINKER) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
else
ifneq (,$(HOST_CPPSRCS)$(USE_HOST_CXX))
$(EXPAND_LIBS_EXEC) -- $(HOST_CXX) $(HOST_OUTOPTION)$@ $(HOST_CXX_LDFLAGS) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS)
$(HOST_CXX) $(HOST_OUTOPTION)$@ $(HOST_CXX_LDFLAGS) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS)
else
$(EXPAND_LIBS_EXEC) -- $(HOST_CC) $(HOST_OUTOPTION)$@ $(HOST_C_LDFLAGS) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS)
$(HOST_CC) $(HOST_OUTOPTION)$@ $(HOST_C_LDFLAGS) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS)
endif
endif
ifndef CROSS_COMPILE
$(call CHECK_STDCXX,$@)
endif
$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(LIBRARY): $(OBJS) $(STATIC_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(REPORT_BUILD)
# Always remove both library and library descriptor
$(RM) $(REAL_LIBRARY) $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX)
$(EXPAND_AR) $(AR_FLAGS) $(OBJS) $(STATIC_LIBS)
$(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
# When we only build a library descriptor, blow out any existing library
$(REPORT_BUILD)
$(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY))
$(EXPAND_LIBS_GEN) -o $@ $(OBJS) $(STATIC_LIBS)
$(RM) $(REAL_LIBRARY)
$(AR) $(AR_FLAGS) $(OBJS) $($@_OBJS)
ifeq ($(OS_ARCH),WINNT)
# Import libraries are created by the rules creating shared libraries.
@ -673,19 +668,19 @@ endif
$(HOST_LIBRARY): $(HOST_OBJS) Makefile
$(REPORT_BUILD)
$(RM) $@
$(EXPAND_LIBS_EXEC) --extract -- $(HOST_AR) $(HOST_AR_FLAGS) $(HOST_OBJS)
$(HOST_AR) $(HOST_AR_FLAGS) $(HOST_OBJS)
# On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files,
# so instead of deleting .o files after repacking them into a dylib, we make
# symlinks back to the originals. The symlinks are a no-op for stabs debugging,
# so no need to conditionalize on OS version or debugging format.
$(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(STATIC_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(REPORT_BUILD)
ifndef INCREMENTAL_LINKER
$(RM) $@
endif
$(EXPAND_MKSHLIB) $(OBJS) $(RESFILE) $(LDFLAGS) $(STATIC_LIBS) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(OS_LIBS)
$(MKSHLIB) $($@_OBJS) $(RESFILE) $(LDFLAGS) $(STATIC_LIBS) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(OS_LIBS)
$(call CHECK_BINARY,$@)
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))

Просмотреть файл

@ -1,5 +1,4 @@
[test_mozbuild_reading.py]
[unit-expandlibs.py]
[unit-mozunit.py]
[unit-nsinstall.py]
[unit-printprereleasesuffix.py]

Просмотреть файл

@ -1,431 +0,0 @@
import subprocess
import unittest
import sys
import os
import imp
from tempfile import mkdtemp
from shutil import rmtree
import mozunit
from UserString import UserString
# Create a controlled configuration for use by expandlibs
config_win = {
'AR': 'lib',
'AR_EXTRACT': '',
'DLL_PREFIX': '',
'LIB_PREFIX': '',
'OBJ_SUFFIX': '.obj',
'LIB_SUFFIX': '.lib',
'DLL_SUFFIX': '.dll',
'IMPORT_LIB_SUFFIX': '.lib',
'LIBS_DESC_SUFFIX': '.desc',
'EXPAND_LIBS_LIST_STYLE': 'list',
}
config_unix = {
'AR': 'ar',
'AR_EXTRACT': 'ar -x',
'DLL_PREFIX': 'lib',
'LIB_PREFIX': 'lib',
'OBJ_SUFFIX': '.o',
'LIB_SUFFIX': '.a',
'DLL_SUFFIX': '.so',
'IMPORT_LIB_SUFFIX': '',
'LIBS_DESC_SUFFIX': '.desc',
'EXPAND_LIBS_LIST_STYLE': 'linkerscript',
}
config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config')
from expandlibs import LibDescriptor, ExpandArgs, relativize
from expandlibs_gen import generate
from expandlibs_exec import ExpandArgsMore, SectionFinder
def Lib(name):
return config.LIB_PREFIX + name + config.LIB_SUFFIX
def Obj(name):
return name + config.OBJ_SUFFIX
def Dll(name):
return config.DLL_PREFIX + name + config.DLL_SUFFIX
def ImportLib(name):
if not len(config.IMPORT_LIB_SUFFIX): return Dll(name)
return config.LIB_PREFIX + name + config.IMPORT_LIB_SUFFIX
class TestRelativize(unittest.TestCase):
def test_relativize(self):
'''Test relativize()'''
os_path_exists = os.path.exists
def exists(path):
return True
os.path.exists = exists
self.assertEqual(relativize(os.path.abspath(os.curdir)), os.curdir)
self.assertEqual(relativize(os.path.abspath(os.pardir)), os.pardir)
self.assertEqual(relativize(os.path.join(os.curdir, 'a')), 'a')
self.assertEqual(relativize(os.path.join(os.path.abspath(os.curdir), 'a')), 'a')
# relativize is expected to return the absolute path if it is shorter
self.assertEqual(relativize(os.sep), os.sep)
os.path.exists = os.path.exists
class TestLibDescriptor(unittest.TestCase):
def test_serialize(self):
'''Test LibDescriptor's serialization'''
desc = LibDescriptor()
desc[LibDescriptor.KEYS[0]] = ['a', 'b']
self.assertEqual(str(desc), "{0} = a b".format(LibDescriptor.KEYS[0]))
desc['unsupported-key'] = ['a']
self.assertEqual(str(desc), "{0} = a b".format(LibDescriptor.KEYS[0]))
desc[LibDescriptor.KEYS[1]] = ['c', 'd', 'e']
self.assertEqual(str(desc),
"{0} = a b\n{1} = c d e"
.format(LibDescriptor.KEYS[0], LibDescriptor.KEYS[1]))
desc[LibDescriptor.KEYS[0]] = []
self.assertEqual(str(desc), "{0} = c d e".format(LibDescriptor.KEYS[1]))
def test_read(self):
'''Test LibDescriptor's initialization'''
desc_list = ["# Comment",
"{0} = a b".format(LibDescriptor.KEYS[1]),
"", # Empty line
"foo = bar", # Should be discarded
"{0} = c d e".format(LibDescriptor.KEYS[0])]
desc = LibDescriptor(desc_list)
self.assertEqual(desc[LibDescriptor.KEYS[1]], ['a', 'b'])
self.assertEqual(desc[LibDescriptor.KEYS[0]], ['c', 'd', 'e'])
self.assertEqual(False, 'foo' in desc)
def wrap_method(conf, wrapped_method):
'''Wrapper used to call a test with a specific configuration'''
def _method(self):
for key in conf:
setattr(config, key, conf[key])
self.init()
try:
wrapped_method(self)
except:
raise
finally:
self.cleanup()
return _method
class ReplicateTests(type):
'''Replicates tests for unix and windows variants'''
def __new__(cls, clsName, bases, dict):
for name in [key for key in dict if key.startswith('test_')]:
dict[name + '_unix'] = wrap_method(config_unix, dict[name])
dict[name + '_unix'].__doc__ = dict[name].__doc__ + ' (unix)'
dict[name + '_win'] = wrap_method(config_win, dict[name])
dict[name + '_win'].__doc__ = dict[name].__doc__ + ' (win)'
del dict[name]
return type.__new__(cls, clsName, bases, dict)
class TestCaseWithTmpDir(unittest.TestCase):
__metaclass__ = ReplicateTests
def init(self):
self.tmpdir = os.path.abspath(mkdtemp(dir=os.curdir))
def cleanup(self):
rmtree(self.tmpdir)
def touch(self, files):
for f in files:
open(f, 'w').close()
def tmpfile(self, *args):
return os.path.join(self.tmpdir, *args)
class TestExpandLibsGen(TestCaseWithTmpDir):
def test_generate(self):
'''Test library descriptor generation'''
files = [self.tmpfile(f) for f in
[Lib('a'), Obj('b'), Lib('c'), Obj('d'), Obj('e'), Lib('f')]]
self.touch(files[:-1])
self.touch([files[-1] + config.LIBS_DESC_SUFFIX])
desc = generate(files)
self.assertEqual(desc['OBJS'], [self.tmpfile(Obj(s)) for s in ['b', 'd', 'e']])
self.assertEqual(desc['LIBS'], [self.tmpfile(Lib(s)) for s in ['a', 'c', 'f']])
self.assertRaises(Exception, generate, files + [self.tmpfile(Obj('z'))])
self.assertRaises(Exception, generate, files + [self.tmpfile(Lib('y'))])
class TestExpandInit(TestCaseWithTmpDir):
def init(self):
''' Initializes test environment for library expansion tests'''
super(TestExpandInit, self).init()
# Create 2 fake libraries, each containing 3 objects, and the second
# including the first one and another library.
os.mkdir(self.tmpfile('libx'))
os.mkdir(self.tmpfile('liby'))
self.libx_files = [self.tmpfile('libx', Obj(f)) for f in ['g', 'h', 'i']]
self.liby_files = [self.tmpfile('liby', Obj(f)) for f in ['j', 'k', 'l']] + [self.tmpfile('liby', Lib('z'))]
self.touch(self.libx_files + self.liby_files)
with open(self.tmpfile('libx', Lib('x') + config.LIBS_DESC_SUFFIX), 'w') as f:
f.write(str(generate(self.libx_files)))
with open(self.tmpfile('liby', Lib('y') + config.LIBS_DESC_SUFFIX), 'w') as f:
f.write(str(generate(self.liby_files + [self.tmpfile('libx', Lib('x'))])))
# Create various objects and libraries
self.arg_files = [self.tmpfile(f) for f in [Lib('a'), Obj('b'), Obj('c'), Lib('d'), Obj('e')]]
# We always give library names (LIB_PREFIX/SUFFIX), even for
# dynamic/import libraries
self.files = self.arg_files + [self.tmpfile(ImportLib('f'))]
self.arg_files += [self.tmpfile(Lib('f'))]
self.touch(self.files)
def assertRelEqual(self, args1, args2):
self.assertEqual(args1, [relativize(a) for a in args2])
class TestExpandArgs(TestExpandInit):
def test_expand(self):
'''Test library expansion'''
# Expanding arguments means libraries with a descriptor are expanded
# with the descriptor content, and import libraries are used when
# a library doesn't exist
args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files)
# When a library exists at the same time as a descriptor, we still use
# the descriptor.
self.touch([self.tmpfile('libx', Lib('x'))])
args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files)
self.touch([self.tmpfile('liby', Lib('y'))])
args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files)
class TestExpandArgsMore(TestExpandInit):
def test_makelist(self):
'''Test grouping object files in lists'''
# ExpandArgsMore does the same as ExpandArgs
with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files)
# But also has an extra method replacing object files with a list
args.makelist()
# self.files has objects at #1, #2, #4
self.assertRelEqual(args[:3], ['foo', '-bar'] + self.files[:1])
self.assertRelEqual(args[4:], [self.files[3]] + self.files[5:] + [self.tmpfile('liby', Lib('z'))])
# Check the list file content
objs = [f for f in self.files + self.liby_files + self.libx_files if f.endswith(config.OBJ_SUFFIX)]
if config.EXPAND_LIBS_LIST_STYLE == "linkerscript":
self.assertNotEqual(args[3][0], '@')
filename = args[3]
content = ['INPUT("{0}")'.format(relativize(f)) for f in objs]
with open(filename, 'r') as f:
self.assertEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
elif config.EXPAND_LIBS_LIST_STYLE == "list":
self.assertEqual(args[3][0], '@')
filename = args[3][1:]
content = objs
with open(filename, 'r') as f:
self.assertRelEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
tmp = args.tmp
# Check that all temporary files are properly removed
self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
def test_extract(self):
'''Test library extraction'''
# Divert subprocess.call
subprocess_call = subprocess.call
subprocess_check_output = subprocess.check_output
def call(args, **kargs):
if config.AR == 'lib':
self.assertEqual(args[:2], [config.AR, '-NOLOGO'])
self.assertTrue(args[2].startswith('-EXTRACT:'))
extract = [args[2][len('-EXTRACT:'):]]
self.assertTrue(extract)
args = args[3:]
else:
# The command called is always AR_EXTRACT
ar_extract = config.AR_EXTRACT.split()
self.assertEqual(args[:len(ar_extract)], ar_extract)
args = args[len(ar_extract):]
# Remaining argument is always one library
self.assertEqual(len(args), 1)
arg = args[0]
self.assertEqual(os.path.splitext(arg)[1], config.LIB_SUFFIX)
# Simulate file extraction
lib = os.path.splitext(os.path.basename(arg))[0]
if config.AR != 'lib':
extract = [lib, lib + '2']
extract = [os.path.join(kargs['cwd'], f) for f in extract]
if config.AR != 'lib':
extract = [Obj(f) for f in extract]
if not lib in extracted:
extracted[lib] = []
extracted[lib].extend(extract)
self.touch(extract)
subprocess.call = call
def check_output(args, **kargs):
# The command called is always AR
ar = config.AR
self.assertEqual(args[0:3], [ar, '-NOLOGO', '-LIST'])
# Remaining argument is always one library
self.assertRelEqual([os.path.splitext(arg)[1] for arg in args[3:]],
[config.LIB_SUFFIX])
# Simulate LIB -NOLOGO -LIST
lib = os.path.splitext(os.path.basename(args[3]))[0]
return '%s\n%s\n' % (Obj(lib), Obj(lib + '2'))
subprocess.check_output = check_output
# ExpandArgsMore does the same as ExpandArgs
self.touch([self.tmpfile('liby', Lib('y'))])
for iteration in (1, 2):
with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
files = self.files + self.liby_files + self.libx_files
self.assertRelEqual(args, ['foo', '-bar'] + files)
extracted = {}
# ExpandArgsMore also has an extra method extracting static libraries
# when possible
args.extract()
# With AR_EXTRACT, it uses the descriptors when there are, and
# actually
# extracts the remaining libraries
extracted_args = []
for f in files:
if f.endswith(config.LIB_SUFFIX):
base = os.path.splitext(os.path.basename(f))[0]
# On the first iteration, we test the behavior of
# extracting archives that don't have a copy of their
# contents next to them, which is to use the file
# extracted from the archive in a temporary directory.
# On the second iteration, we test extracting archives
# that do have a copy of their contents next to them,
# in which case those contents are used instead of the
# temporarily extracted files.
if iteration == 1:
extracted_args.extend(sorted(extracted[base]))
else:
dirname = os.path.dirname(f[len(self.tmpdir)+1:])
if base.endswith('f'):
dirname = os.path.join(dirname, 'foo', 'bar')
extracted_args.extend([self.tmpfile(dirname, Obj(base)), self.tmpfile(dirname, Obj(base + '2'))])
else:
extracted_args.append(f)
self.assertRelEqual(args, ['foo', '-bar'] + extracted_args)
tmp = args.tmp
# Check that all temporary files are properly removed
self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
# Create archives contents next to them for the second iteration.
base = os.path.splitext(Lib('_'))[0]
self.touch(self.tmpfile(Obj(base.replace('_', suffix))) for suffix in ('a', 'a2', 'd', 'd2'))
try:
os.makedirs(self.tmpfile('foo', 'bar'))
except:
pass
self.touch(self.tmpfile('foo', 'bar', Obj(base.replace('_', suffix))) for suffix in ('f', 'f2'))
self.touch(self.tmpfile('liby', Obj(base.replace('_', suffix))) for suffix in ('z', 'z2'))
# Restore subprocess.call and subprocess.check_output
subprocess.call = subprocess_call
subprocess.check_output = subprocess_check_output
class FakeProcess(object):
def __init__(self, out, err = ''):
self.out = out
self.err = err
def communicate(self):
return (self.out, self.err)
OBJDUMPS = {
'foo.o': '''
00000000 g F .text\t00000001 foo
00000000 g F .text._Z6foobarv\t00000001 _Z6foobarv
00000000 g F .text.hello\t00000001 hello
00000000 g F .text._ZThn4_6foobarv\t00000001 _ZThn4_6foobarv
''',
'bar.o': '''
00000000 g F .text.hi\t00000001 hi
00000000 g F .text.hot._Z6barbazv\t00000001 .hidden _Z6barbazv
''',
}
PRINT_ICF = '''
ld: ICF folding section '.text.hello' in file 'foo.o'into '.text.hi' in file 'bar.o'
ld: ICF folding section '.foo' in file 'foo.o'into '.foo' in file 'bar.o'
'''
class SubprocessPopen(object):
def __init__(self, test):
self.test = test
def __call__(self, args, stdout = None, stderr = None):
self.test.assertEqual(stdout, subprocess.PIPE)
self.test.assertEqual(stderr, subprocess.PIPE)
if args[0] == 'objdump':
self.test.assertEqual(args[1], '-t')
self.test.assertTrue(args[2] in OBJDUMPS)
return FakeProcess(OBJDUMPS[args[2]])
else:
return FakeProcess('', PRINT_ICF)
class TestSectionFinder(unittest.TestCase):
def test_getSections(self):
'''Test SectionFinder'''
# Divert subprocess.Popen
subprocess_popen = subprocess.Popen
subprocess.Popen = SubprocessPopen(self)
config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
config.OBJ_SUFFIX = '.o'
config.LIB_SUFFIX = '.a'
finder = SectionFinder(['foo.o', 'bar.o'])
self.assertEqual(finder.getSections('foobar'), [])
self.assertEqual(finder.getSections('_Z6barbazv'), ['.text.hot._Z6barbazv'])
self.assertEqual(finder.getSections('_Z6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv'])
self.assertEqual(finder.getSections('_ZThn4_6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv'])
subprocess.Popen = subprocess_popen
class TestSymbolOrder(unittest.TestCase):
def test_getOrderedSections(self):
'''Test ExpandMoreArgs' _getOrderedSections'''
# Divert subprocess.Popen
subprocess_popen = subprocess.Popen
subprocess.Popen = SubprocessPopen(self)
config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
config.OBJ_SUFFIX = '.o'
config.LIB_SUFFIX = '.a'
config.LD_PRINT_ICF_SECTIONS = ''
args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
self.assertEqual(args._getOrderedSections(['_Z6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv'])
self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv'])
subprocess.Popen = subprocess_popen
def test_getFoldedSections(self):
'''Test ExpandMoreArgs' _getFoldedSections'''
# Divert subprocess.Popen
subprocess_popen = subprocess.Popen
subprocess.Popen = SubprocessPopen(self)
config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections'
args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
self.assertEqual(args._getFoldedSections(), {'.text.hello': ['.text.hi'], '.text.hi': ['.text.hello']})
subprocess.Popen = subprocess_popen
def test_getOrderedSectionsWithICF(self):
'''Test ExpandMoreArgs' _getOrderedSections, with ICF'''
# Divert subprocess.Popen
subprocess_popen = subprocess.Popen
subprocess.Popen = SubprocessPopen(self)
config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
config.OBJ_SUFFIX = '.o'
config.LIB_SUFFIX = '.a'
config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections'
args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
self.assertEqual(args._getOrderedSections(['hello', '_Z6barbazv']), ['.text.hello', '.text.hi', '.text.hot._Z6barbazv'])
self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', 'hi', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hi', '.text.hello', '.text.hot._Z6barbazv'])
subprocess.Popen = subprocess_popen
if __name__ == '__main__':
mozunit.main(runwith='unittest')

Просмотреть файл

@ -27,8 +27,12 @@ from mozbuild.frontend.data import (
FinalTargetFiles,
GeneratedSources,
GnProjectData,
HostLibrary,
HostRustLibrary,
IPDLCollection,
RustLibrary,
SharedLibrary,
StaticLibrary,
UnifiedSources,
XPIDLFile,
WebIDLCollection,
@ -40,7 +44,10 @@ from mozbuild.jar import (
from mozbuild.preprocessor import Preprocessor
from mozpack.chrome.manifest import parse_manifest_line
from mozbuild.util import group_unified_files
from mozbuild.util import (
group_unified_files,
mkdir,
)
class XPIDLManager(object):
"""Helps manage XPCOM IDLs in the context of the build system."""
@ -196,6 +203,87 @@ class CommonBackend(BuildBackend):
}
json.dump(d, fh, sort_keys=True, indent=4)
def _expand_libs(self, input_bin):
os_libs = []
shared_libs = []
static_libs = []
objs = []
seen_objs = set()
seen_libs = set()
def add_objs(lib):
for o in lib.objs:
if o not in seen_objs:
seen_objs.add(o)
objs.append(o)
def expand(lib, recurse_objs, system_libs):
if isinstance(lib, StaticLibrary):
if lib.no_expand_lib:
static_libs.append(lib)
recurse_objs = False
elif recurse_objs:
add_objs(lib)
for l in lib.linked_libraries:
expand(l, recurse_objs, system_libs)
if system_libs:
for l in lib.linked_system_libs:
if l not in seen_libs:
seen_libs.add(l)
os_libs.append(l)
elif isinstance(lib, SharedLibrary):
if lib not in seen_libs:
seen_libs.add(lib)
shared_libs.append(lib)
add_objs(input_bin)
system_libs = not isinstance(input_bin, StaticLibrary)
for lib in input_bin.linked_libraries:
if isinstance(lib, RustLibrary):
continue
elif isinstance(lib, StaticLibrary):
expand(lib, True, system_libs)
elif isinstance(lib, SharedLibrary):
if lib not in seen_libs:
seen_libs.add(lib)
shared_libs.append(lib)
for lib in input_bin.linked_system_libs:
if lib not in seen_libs:
seen_libs.add(lib)
os_libs.append(lib)
return objs, shared_libs, os_libs, static_libs
def _make_list_file(self, objdir, objs, name):
if not objs:
return None
list_style = self.environment.substs.get('EXPAND_LIBS_LIST_STYLE')
list_file_path = mozpath.join(objdir, name)
objs = [os.path.relpath(o, objdir) for o in objs]
if list_style == 'linkerscript':
ref = list_file_path
content = '\n'.join('INPUT("%s")' % o for o in objs)
elif list_style == 'filelist':
ref = "-Wl,-filelist," + list_file_path
content = '\n'.join(objs)
elif list_style == 'list':
ref = "@" + list_file_path
content = '\n'.join(objs)
else:
return None
mkdir(objdir)
with self._write_file(list_file_path) as fh:
fh.write(content)
return ref
def _handle_generated_sources(self, files):
self._generated_sources.update(mozpath.relpath(f, self.environment.topobjdir) for f in files)

Просмотреть файл

@ -1324,16 +1324,6 @@ class RecursiveMakeBackend(CommonBackend):
self.environment.topobjdir), obj.KIND)
def _process_linked_libraries(self, obj, backend_file):
def write_shared_and_system_libs(lib):
for l in lib.linked_libraries:
if isinstance(l, (StaticLibrary, RustLibrary)):
write_shared_and_system_libs(l)
else:
backend_file.write_once('SHARED_LIBS += %s/%s\n'
% (pretty_relpath(l), l.import_name))
for l in lib.linked_system_libs:
backend_file.write_once('OS_LIBS += %s\n' % l)
def pretty_relpath(lib):
return '$(DEPTH)/%s' % mozpath.relpath(lib.objdir, topobjdir)
@ -1342,36 +1332,53 @@ class RecursiveMakeBackend(CommonBackend):
build_target = self._build_target_for_obj(obj)
self._compile_graph[build_target]
objs, shared_libs, os_libs, static_libs = self._expand_libs(obj)
if obj.KIND == 'target':
obj_target = obj.name
if isinstance(obj, Program):
obj_target = self._pretty_path(obj.output_path, backend_file)
objs_ref = ' \\\n '.join(os.path.relpath(o, obj.objdir)
for o in objs)
# Don't bother with a list file if we're only linking objects built
# in this directory or building a real static library. This
# accommodates clang-plugin, where we would otherwise pass an
# incorrect list file format to the host compiler as well as when
# creating an archive with AR, which doesn't understand list files.
if (objs == obj.objs and not isinstance(obj, StaticLibrary) or
isinstance(obj, StaticLibrary) and obj.no_expand_lib):
backend_file.write_once('%s_OBJS := %s\n' %
(obj.name, objs_ref))
backend_file.write_once('%s: %s\n' % (obj_target, objs_ref))
elif not isinstance(obj, StaticLibrary):
list_file_path = '%s.list' % obj.name.replace('.', '_')
list_file_ref = self._make_list_file(obj.objdir, objs,
list_file_path)
backend_file.write_once('%s_OBJS := %s\n' %
(obj.name, list_file_ref))
backend_file.write_once('%s: %s\n' % (obj_target, list_file_path))
backend_file.write_once('%s: %s\n' % (obj_target, objs_ref))
for lib in shared_libs:
backend_file.write_once('SHARED_LIBS += %s/%s\n' %
(pretty_relpath(lib), lib.import_name))
for lib in static_libs:
backend_file.write_once('STATIC_LIBS += %s/%s\n' %
(pretty_relpath(lib), lib.import_name))
for lib in os_libs:
if obj.KIND == 'target':
backend_file.write_once('OS_LIBS += %s\n' % lib)
else:
backend_file.write_once('HOST_EXTRA_LIBS += %s\n' % lib)
for lib in obj.linked_libraries:
if not isinstance(lib, ExternalLibrary):
self._compile_graph[build_target].add(
self._build_target_for_obj(lib))
relpath = pretty_relpath(lib)
if isinstance(obj, Library):
if isinstance(lib, RustLibrary):
# We don't need to do anything here; we will handle
# linkage for any RustLibrary elsewhere.
continue
elif isinstance(lib, StaticLibrary):
backend_file.write_once('STATIC_LIBS += %s/%s\n'
% (relpath, lib.import_name))
if isinstance(obj, SharedLibrary):
write_shared_and_system_libs(lib)
elif isinstance(obj, SharedLibrary):
backend_file.write_once('SHARED_LIBS += %s/%s\n'
% (relpath, lib.import_name))
elif isinstance(obj, (Program, SimpleProgram)):
if isinstance(lib, StaticLibrary):
backend_file.write_once('STATIC_LIBS += %s/%s\n'
% (relpath, lib.import_name))
write_shared_and_system_libs(lib)
else:
backend_file.write_once('SHARED_LIBS += %s/%s\n'
% (relpath, lib.import_name))
elif isinstance(obj, (HostLibrary, HostProgram, HostSimpleProgram)):
assert isinstance(lib, (HostLibrary, HostRustLibrary))
backend_file.write_once('HOST_LIBS += %s/%s\n'
% (relpath, lib.import_name))
if isinstance(lib, (HostLibrary, HostRustLibrary)):
backend_file.write_once('HOST_LIBS += %s/%s\n' %
(pretty_relpath(lib), lib.import_name))
# We have to link any Rust libraries after all intermediate static
# libraries have been listed to ensure that the Rust libraries are
@ -1379,12 +1386,6 @@ class RecursiveMakeBackend(CommonBackend):
if isinstance(obj, SharedLibrary):
self._process_rust_libraries(obj, backend_file, pretty_relpath)
for lib in obj.linked_system_libs:
if obj.KIND == 'target':
backend_file.write_once('OS_LIBS += %s\n' % lib)
else:
backend_file.write_once('HOST_EXTRA_LIBS += %s\n' % lib)
# Process library-based defines
self._process_defines(obj.lib_defines, backend_file)

Просмотреть файл

@ -214,6 +214,7 @@ CONFIGS = defaultdict(lambda: {
'BIN_SUFFIX': '.exe',
'DLL_SUFFIX': '.so',
'OBJ_SUFFIX': 'o',
'EXPAND_LIBS_LIST_STYLE': 'list',
},
},
})

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,8 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
SOURCES += [
'bar_helper1.cpp',
]
FINAL_LIBRARY = 'bar'

Просмотреть файл

@ -3,6 +3,11 @@
SOURCES += [
'bar1.cc',
'bar2.cc',
]
DIRS += [
'bar_helper',
]
FINAL_LIBRARY = 'bar'

Просмотреть файл

@ -1054,30 +1054,24 @@ class TestRecursiveMakeBackend(BackendTester):
env = self._consume('linkage', RecursiveMakeBackend)
expected_linkage = {
'prog': {
'SHARED_LIBS': ['$(DEPTH)/shared/baz', '$(DEPTH)/prog/qux/qux'],
'STATIC_LIBS': ['$(DEPTH)/static/bar%s' % env.lib_suffix],
'SHARED_LIBS': ['$(DEPTH)/prog/qux/qux.so',
'$(DEPTH)/shared/baz.so'],
'STATIC_LIBS': ['$(DEPTH)/real/foo.a'],
'OS_LIBS': ['-lfoo', '-lbaz', '-lbar'],
},
'shared': {
'OS_LIBS': ['-lfoo'],
'SHARED_LIBS': ['$(DEPTH)/prog/qux/qux'],
'STATIC_LIBS': ['$(DEPTH)/shared/baz/shared_baz%s' %
env.lib_suffix],
'SHARED_LIBS': ['$(DEPTH)/prog/qux/qux.so'],
'STATIC_LIBS': [],
},
'static': {
'STATIC_LIBS': [
'$(DEPTH)/static/bar/static_bar.a',
'$(DEPTH)/real/foo.a',
],
'STATIC_LIBS': ['$(DEPTH)/real/foo.a'],
'OS_LIBS': ['-lbar'],
'SHARED_LIBS': [],
'SHARED_LIBS': ['$(DEPTH)/prog/qux/qux.so'],
},
'real': {
'STATIC_LIBS': [
'$(DEPTH)/shared/baz_s%s' % env.lib_suffix,
'$(DEPTH)/real/foo/real_foo%s' % env.lib_suffix,
],
'SHARED_LIBS': [],
'STATIC_LIBS': [],
'SHARED_LIBS': ['$(DEPTH)/prog/qux/qux.so'],
'OS_LIBS': ['-lbaz'],
}
}
@ -1095,6 +1089,34 @@ class TestRecursiveMakeBackend(BackendTester):
for line in actual_linkage[name]:
self.assertNotIn('%s +=' % var, line)
def test_list_files(self):
env = self._consume('linkage', RecursiveMakeBackend)
expected_list_files = {
'prog/MyProgram_exe.list': [
'../static/bar/bar1.o',
'../static/bar/bar2.o',
'../static/bar/bar_helper/bar_helper1.o',
],
'shared/baz_so.list': [
'baz/baz1.o',
],
}
actual_list_files = {}
for name in expected_list_files.keys():
with open(os.path.join(env.topobjdir, name), 'rb') as fh:
actual_list_files[name] = [mozpath.normsep(line.rstrip())
for line in fh.readlines()]
for name in expected_list_files:
self.assertEqual(actual_list_files[name],
expected_list_files[name])
# We don't produce a list file for a shared library composed only of
# object files in its directory, but instead list them in a variable.
with open(os.path.join(env.topobjdir, 'prog', 'qux', 'backend.mk'), 'rb') as fh:
lines = [line.rstrip() for line in fh.readlines()]
self.assertIn('qux.so_OBJS := qux1.o', lines)
def test_jar_manifests(self):
env = self._consume('jar-manifests', RecursiveMakeBackend)