Skip to content

Commit f5995b4

Browse files
authored
Allow packaging step to specify an explicit binutils format, and add some configuration options to build_desktop (#241)
Specifying an explicit binutils format fixes an issue in Windows builds where some libraries would be autodetected, but then would create invalid output libraries since the target format wasn't specified. This was causing a linker issue with absl in Firestore with vector deleting destructors not being found, because certain absl libraries were not created valid by merge_libraries. In particular, Windows libraries are now set to be output in pe-bigobj-x86-64/pe-bigobj-i386 format, since Firestore in particular has >65K sections and requires bigobj. There's no harm in making all the other libraries the same format. This PR also adds a couple of flags to build_desktop.py: It adds support for performing a verbose build during packaging, in case it's needed for further debugging. Adds a flag for disabling vcpkg, although we don't yet use it as we require vcpkg to get the "protoc" executable. It also includes a bit of code in the workflow to handle Mac arm64 although we don't yet build for that platform.
1 parent 9c5e5a5 commit f5995b4

File tree

5 files changed

+108
-42
lines changed

5 files changed

+108
-42
lines changed

.github/workflows/cpp-packaging.yml

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ on:
1111
preserveIntermediateArtifacts:
1212
description: 'preserve intermediate artifacts?'
1313
default: 0
14+
verboseBuild:
15+
description: 'verbose build?'
16+
default: 0
1417
skipIntegrationTests:
1518
description: 'skip integration tests?'
1619
default: 0
@@ -60,6 +63,12 @@ jobs:
6063
github.event.inputs.downloadPublicVersion == '' && github.event.inputs.downloadPreviousRun == ''
6164
run: echo "::warning ::Intermediate artifacts will be preserved."
6265

66+
- name: log if verbose build enabled
67+
if: |
68+
github.event.inputs.verboseBuild != 0 && github.event.inputs.verboseBuild != '' &&
69+
github.event.inputs.downloadPublicVersion == '' && github.event.inputs.downloadPreviousRun == ''
70+
run: echo "::warning ::Verbose build enabled."
71+
6372
build_tools:
6473
name: build-tools-${{ matrix.tools_platform }}
6574
runs-on: ${{ matrix.os }}
@@ -320,7 +329,11 @@ jobs:
320329
- name: Build desktop SDK
321330
shell: bash
322331
run: |
323-
python scripts/gha/build_desktop.py --arch "${{ matrix.architecture }}" --config "${{ matrix.build_type }}" --msvc_runtime_library "${{ matrix.msvc_runtime }}" --linux_abi "${{ matrix.linux_abi }}" --build_dir out-${{ env.SDK_NAME }} ${{ matrix.additional_build_flags }}
332+
verbose_flag=
333+
if [[ -n "${{ github.event.inputs.verboseBuild }}" && "${{ github.event.inputs.verboseBuild }}" -ne 0 ]]; then
334+
verbose_flag=--verbose
335+
fi
336+
python scripts/gha/build_desktop.py --arch "${{ matrix.architecture }}" --config "${{ matrix.build_type }}" --msvc_runtime_library "${{ matrix.msvc_runtime }}" --linux_abi "${{ matrix.linux_abi }}" --build_dir out-${{ env.SDK_NAME }} ${verbose_flag} ${{ matrix.additional_build_flags }}
324337
# Make a list of all the source files, for debugging purposes.
325338
cd out-${{ env.SDK_NAME }}
326339
find .. -type f -print > src_file_list.txt
@@ -433,12 +446,30 @@ jobs:
433446
else
434447
tools_platform=darwin
435448
fi
449+
verbose_flag=
450+
if [[ -n "${{ github.event.inputs.verboseBuild }}" && "${{ github.event.inputs.verboseBuild }}" -ne 0 ]]; then
451+
verbose_flag=-v
452+
fi
453+
declare -a additional_flags
436454
tar -xvzf artifacts/packaging-tools-${tools_platform}/packaging-tools.tgz -C bin
437455
chmod -R u+x bin
438456
for pkg in artifacts/firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix }}*-build/*.tgz; do
439457
# determine the build variant based on the artifact filename
440458
variant=$(sdk-src/build_scripts/desktop/get_variant.sh "${pkg}")
441-
sdk-src/build_scripts/desktop/package.sh -b ${pkg} -o firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix }}-package -p ${{ matrix.sdk_platform }} -t bin -d ${variant} -P python3 -j
459+
additional_flags=(${verbose_flag})
460+
# Several build targets require explicitly-set binutils format to be passed
461+
# to package.sh (and thus, to merge_libraries).
462+
if [[ "${{ matrix.sdk_platform }}" == "darwin" && "${variant}" == "arm64" ]]; then
463+
# MacOS ARM
464+
additional_flags+=(-f mach-o-arm64)
465+
elif [[ "${{ matrix.sdk_platform }}" == "windows" && "${variant}" == *"/x64/"* ]]; then
466+
# Windows x64
467+
additional_flags+=(-f pe-x86-64,pe-bigobj-x86-64)
468+
elif [[ "${{ matrix.sdk_platform }}" == "windows" && "${variant}" == *"/x86/"* ]]; then
469+
# Windows x86
470+
additional_flags+=(-f pe-i386,pe-bigobj-i386)
471+
fi
472+
sdk-src/build_scripts/desktop/package.sh -b ${pkg} -o firebase-cpp-sdk-${{ matrix.sdk_platform }}${{ matrix.suffix }}-package -p ${{ matrix.sdk_platform }} -t bin -d ${variant} -P python3 -j ${additional_flags[*]}
442473
done
443474
if [[ "${{ matrix.sdk_platform }}" == "darwin" ]]; then
444475
# Darwin has a final step after all the variants are done,

build_scripts/desktop/package.sh

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ options:
1313
-m, merge_libraries.py path default: <script dir>/../../scripts/merge_libraries.py
1414
-P, python command default: python
1515
-t, packaging tools directory default: ~/bin
16+
-f, binutils format default: [auto-detect]
1617
-j, run merge_libraries jobs in parallel
1718
-v, enable verbose mode
1819
example:
@@ -29,6 +30,7 @@ root_dir=$(cd $(dirname $0)/../..; pwd -P)
2930
merge_libraries_script=${root_dir}/scripts/merge_libraries.py
3031
tools_path=~/bin
3132
built_sdk_tarfile=
33+
binutils_format=
3234
temp_dir=
3335
run_in_parallel=0
3436

@@ -44,8 +46,11 @@ abspath(){
4446
fi
4547
}
4648

47-
while getopts "b:o:p:d:m:P:t:hjv" opt; do
49+
while getopts "f:b:o:p:d:m:P:t:hjv" opt; do
4850
case $opt in
51+
f)
52+
binutils_format=$OPTARG
53+
;;
4954
b)
5055
built_sdk_path=$OPTARG
5156
;;
@@ -252,6 +257,11 @@ fi
252257
if [[ ${verbose} -eq 1 ]]; then
253258
merge_libraries_params+=(--verbosity=3)
254259
fi
260+
if [[ -n "${binutils_format}" ]]; then
261+
merge_libraries_params+=(--force_binutils_target="${binutils_format}")
262+
fi
263+
264+
255265

256266
if [[ ! -x "${binutils_objcopy}" || ! -x "${binutils_ar}" || ! -x "${binutils_nm}" ]]; then
257267
echo "Packaging tools not found at path '${tools_path}'."
@@ -327,12 +337,12 @@ for product in ${product_list[*]}; do
327337
rm -f "${outfile}"
328338
if [[ ${verbose} -eq 1 ]]; then
329339
echo "${python_cmd}" "${merge_libraries_script}" \
330-
${merge_libraries_params[*]} \
340+
${merge_libraries_params[*]} \
331341
${cache_param} \
332-
--output="${outfile}" \
333-
--scan_libs="${allfiles}" \
334-
--hide_c_symbols="${deps_hidden}" \
335-
${libfile_src} ${deps[*]}
342+
--output="${outfile}" \
343+
--scan_libs="${allfiles}" \
344+
--hide_c_symbols="${deps_hidden}" \
345+
${libfile_src} ${deps[*]}
336346
fi
337347
# Place the merge command in a script so we can optionally run them in parallel.
338348
echo "#!/bin/bash -e" > "${merge_libraries_tmp}/merge_${product}.sh"
@@ -342,7 +352,7 @@ for product in ${product_list[*]}; do
342352
echo "echo \"${libfile_out}\"" >> "${merge_libraries_tmp}/merge_${product}.sh"
343353
fi
344354
if [[ ! -z ${deps_basenames[*]} ]]; then
345-
echo -n >> "${merge_libraries_tmp}/merge_${product}.sh"
355+
echo -n >> "${merge_libraries_tmp}/merge_${product}.sh"
346356
fi
347357
echo >> "${merge_libraries_tmp}/merge_${product}.sh"
348358
echo "\"${python_cmd}\" \\

cmake/external_rules.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ function(build_external_dependencies)
148148
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}")
149149
endif()
150150

151+
if (CMAKE_VERBOSE_MAKEFILE)
152+
# If verbose mode was enabled, pass it on
153+
set(CMAKE_SUB_CONFIGURE_OPTIONS ${CMAKE_SUB_CONFIGURE_OPTIONS}
154+
"-DCMAKE_VERBOSE_MAKEFILE=${CMAKE_VERBOSE_MAKEFILE}")
155+
endif()
156+
151157
if(APPLE)
152158
# Propagate MacOS build flags.
153159
if(CMAKE_OSX_ARCHITECTURES)

scripts/gha/build_desktop.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ def install_cpp_dependencies_with_vcpkg(arch, msvc_runtime_library, cleanup=True
141141
utils.clean_vcpkg_temp_data()
142142

143143
def cmake_configure(build_dir, arch, msvc_runtime_library='static', linux_abi='legacy',
144-
build_tests=True, config=None, target_format=None):
144+
build_tests=True, config=None, target_format=None,
145+
disable_vcpkg=False, verbose=False):
145146
""" CMake configure.
146147
147148
If you are seeing problems when running this multiple times,
@@ -156,6 +157,8 @@ def cmake_configure(build_dir, arch, msvc_runtime_library='static', linux_abi='l
156157
config (str): Release/Debug config.
157158
If its not specified, cmake's default is used (most likely Debug).
158159
target_format (str): If specified, build for this targetformat ('frameworks' or 'libraries').
160+
disable_vcpkg (bool): If True, skip vcpkg and just use CMake for deps.
161+
verbose (bool): If True, enable verbose mode in the CMake file.
159162
"""
160163
cmd = ['cmake', '-S', '.', '-B', build_dir]
161164

@@ -170,19 +173,18 @@ def cmake_configure(build_dir, arch, msvc_runtime_library='static', linux_abi='l
170173
# workaround, absl doesn't build without tests enabled
171174
cmd.append('-DBUILD_TESTING=off')
172175

173-
if utils.is_linux_os() and arch == 'x86':
174-
# Use a separate cmake toolchain for cross compiling linux x86 builds
175-
vcpkg_toolchain_file_path = os.path.join(os.getcwd(), 'external', 'vcpkg',
176-
'scripts', 'buildsystems', 'linux_32.cmake')
177-
else:
178-
vcpkg_toolchain_file_path = os.path.join(os.getcwd(), 'external',
179-
'vcpkg', 'scripts',
180-
'buildsystems', 'vcpkg.cmake')
181-
182-
cmd.append('-DCMAKE_TOOLCHAIN_FILE={0}'.format(vcpkg_toolchain_file_path))
183-
184-
vcpkg_triplet = utils.get_vcpkg_triplet(arch, msvc_runtime_library)
185-
cmd.append('-DVCPKG_TARGET_TRIPLET={0}'.format(vcpkg_triplet))
176+
if not disable_vcpkg:
177+
if utils.is_linux_os() and arch == 'x86':
178+
# Use a separate cmake toolchain for cross compiling linux x86 builds
179+
vcpkg_toolchain_file_path = os.path.join(os.getcwd(), 'external', 'vcpkg',
180+
'scripts', 'buildsystems', 'linux_32.cmake')
181+
else:
182+
vcpkg_toolchain_file_path = os.path.join(os.getcwd(), 'external',
183+
'vcpkg', 'scripts',
184+
'buildsystems', 'vcpkg.cmake')
185+
cmd.append('-DCMAKE_TOOLCHAIN_FILE={0}'.format(vcpkg_toolchain_file_path))
186+
vcpkg_triplet = utils.get_vcpkg_triplet(arch, msvc_runtime_library)
187+
cmd.append('-DVCPKG_TARGET_TRIPLET={0}'.format(vcpkg_triplet))
186188

187189
if utils.is_windows_os():
188190
# If building for x86, we should supply -A Win32 to cmake configure
@@ -202,31 +204,38 @@ def cmake_configure(build_dir, arch, msvc_runtime_library='static', linux_abi='l
202204
cmd.append('-DFIREBASE_XCODE_TARGET_FORMAT={0}'.format(target_format))
203205

204206
cmd.append('-DFIREBASE_USE_BORINGSSL=ON')
207+
208+
# Print out every command while building.
209+
if verbose:
210+
cmd.append('-DCMAKE_VERBOSE_MAKEFILE=1')
211+
205212
utils.run_command(cmd)
206213

207214
def main():
208215
args = parse_cmdline_args()
209216

210217
# Ensure that the submodules are initialized and updated
211218
# Example: vcpkg is a submodule (external/vcpkg)
212-
utils.run_command(['git', 'submodule', 'init'])
213-
utils.run_command(['git', 'submodule', 'update'])
219+
if not args.disable_vcpkg:
220+
utils.run_command(['git', 'submodule', 'init'])
221+
utils.run_command(['git', 'submodule', 'update'])
214222

215223
# To build x86 on x86_64 linux hosts, we also need x86 support libraries
216224
if args.arch == 'x86' and utils.is_linux_os():
217225
install_x86_support_libraries()
218226

219-
# Install C++ dependencies using vcpkg
220-
install_cpp_dependencies_with_vcpkg(args.arch, args.msvc_runtime_library,
221-
cleanup=True)
227+
if not args.disable_vcpkg:
228+
# Install C++ dependencies using vcpkg
229+
install_cpp_dependencies_with_vcpkg(args.arch, args.msvc_runtime_library,
230+
cleanup=True)
222231

223232
if args.vcpkg_step_only:
224233
print("Exiting without building the Firebase C++ SDK as just vcpkg step was requested.")
225234
return
226235

227236
# CMake configure
228237
cmake_configure(args.build_dir, args.arch, args.msvc_runtime_library, args.linux_abi,
229-
args.build_tests, args.config, args.target_format)
238+
args.build_tests, args.config, args.target_format, args.disable_vcpkg, args.verbose)
230239

231240
# CMake build
232241
# cmake --build build -j 8
@@ -238,14 +247,6 @@ def main():
238247
cmd.append('--target')
239248
cmd.extend(args.target)
240249
utils.run_command(cmd)
241-
# Copy libraries from appropriate vcpkg directory to build output
242-
# directory for later inclusion.
243-
vcpkg_path = ('external/vcpkg/installed/%s/%slib/' %
244-
(utils.get_vcpkg_triplet(args.arch, args.msvc_runtime_library),
245-
'debug/' if args.config == 'Debug' else ''))
246-
if (os.path.exists(vcpkg_path)):
247-
shutil.rmtree('vcpkg-libs', ignore_errors=True)
248-
shutil.copytree(vcpkg_path, os.path.join(args.build_dir, 'vcpkg-libs'), )
249250

250251

251252
def parse_cmdline_args():
@@ -257,6 +258,8 @@ def parse_cmdline_args():
257258
help='C++ ABI for Linux (legacy or c++11)')
258259
parser.add_argument('--build_dir', default='build', help='Output build directory')
259260
parser.add_argument('--build_tests', action='store_true', help='Build unit tests too')
261+
parser.add_argument('--verbose', action='store_true', help='Enable verbose CMake builds.')
262+
parser.add_argument('--disable_vcpkg', action='store_true', help='Disable vcpkg and just use CMake.')
260263
parser.add_argument('--vcpkg_step_only', action='store_true', help='Just install cpp packages using vcpkg and exit.')
261264
parser.add_argument('--config', default='Release', help='Release/Debug config')
262265
parser.add_argument('--target', nargs='+', help='A list of CMake build targets (eg: firebase_app firebase_auth)')

scripts/merge_libraries.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
"skip_creating_archives", False,
100100
"Skip creating archive files (.a or .lib) and instead just leave the object "
101101
"files (.o or .obj) in the output directory.")
102+
flags.DEFINE_string("force_binutils_target", None, "Force all binutils calls to "
103+
"use the given target, via the --target flag. If not set, "
104+
"will autodetect target format. If you want to specify "
105+
"different input and output formats, separate them with a comma.")
102106

103107
# Never rename 'std::' by default when --auto_hide_cpp_namespaces is enabled.
104108
IMPLICIT_CPP_NAMESPACES_TO_IGNORE = {"std"}
@@ -266,7 +270,7 @@ def create_archive(output_archive_file, object_files, old_archive=None):
266270
Empty list if there are no errors, or error text if there was an error.
267271
"""
268272
errors = []
269-
if old_archive and FLAGS.platform != "windows":
273+
if old_archive and FLAGS.platform != "windows" and FLAGS.platform != "darwin":
270274
# Copy the old archive to the new archive, then clear the files from it.
271275
# This preserves the file format of the old archive file.
272276
# On Windows, we'll always create a new archive.
@@ -641,6 +645,9 @@ def rename_symbol(symbol):
641645
new_symbol = re.sub("@%s@@" % ns, "@%s@@" % new_ns, new_symbol)
642646
new_renames[symbol] = new_symbol
643647
else:
648+
if FLAGS.platform == "windows" and symbol.startswith("$LN"):
649+
# Don't rename $LN*, those are local symbols.
650+
return new_renames
644651
# C symbol. Just split, rename, and re-join.
645652
(prefix, remainder) = split_symbol(symbol)
646653
new_symbol = prefix + FLAGS.rename_string + remainder
@@ -705,9 +712,10 @@ def move_object_file(src_obj_file, dest_obj_file, redefinition_file=None):
705712
# If we created the output file, remove the input file.
706713
if os.path.isfile(dest_obj_file):
707714
# But first...
708-
if os.path.getsize(src_obj_file) >= 16 and os.path.getsize(
709-
dest_obj_file) >= 16 and (FLAGS.platform == "ios" or
710-
FLAGS.platform == "darwin"):
715+
if (os.path.getsize(src_obj_file) >= 16 and
716+
os.path.getsize(dest_obj_file) >= 16 and
717+
(FLAGS.platform == "ios" or FLAGS.platform == "darwin") and
718+
not binutils_force_target_format):
711719
# Ugly hack time: objcopy doesn't set the CPU subtype correctly on the
712720
# header for Mach-O files. So just overwrite the first 16 bytes of the
713721
# output file with the first 16 bytes of the input file.
@@ -753,7 +761,13 @@ def run_binutils_command(cmdline, error_output=None, ignore_errors=False):
753761
# files use the same format. Also we will need to explicitly specify this
754762
# format when creating an archive with "ar".
755763
# If we've never had to force a format, let binutils autodetect.
756-
output = run_command([cmdline[0]] + ["--target=%s" % binutils_force_target_format] + cmdline[1:],
764+
# Also, we can force a separate input and output target for objcopy, splitting on comma.
765+
target_list = binutils_force_target_format.split(",")
766+
if cmdline[0] == FLAGS.binutils_objcopy_cmd and len(target_list) > 1:
767+
target_params = ["--input-target=%s" % target_list[0], "--output-target=%s" % target_list[1]]
768+
else:
769+
target_params = ["--target=%s" % target_list[0]]
770+
output = run_command([cmdline[0]] + target_params + cmdline[1:],
757771
error_output, ignore_errors)
758772
else:
759773
# Otherwise, if we've never had to force a format, use the default.
@@ -896,6 +910,8 @@ def shutdown_cache():
896910

897911

898912
def main(argv):
913+
global binutils_force_target_format
914+
binutils_force_target_format = FLAGS.force_binutils_target
899915
try:
900916
working_root = None
901917
input_paths = []

0 commit comments

Comments
 (0)