# Copyright (c) 2022 Alex313031 # Shows the output of a given command only on failure, or when VERBOSE is set. log_cmd() { if [ "${VERBOSE:-}" ]; then "$@" else # Record $- into a separate variable because it gets reset in the subshell. FORWARD_SHELL_OPTS=$- ERREXIT=$(echo ${FORWARD_SHELL_OPTS} | grep -o e || true) set +${ERREXIT} CMD_OUTPUT=$("$@" 2>&1) ERRCODE=$? set -${ERREXIT} if [ ${ERRCODE} -ne 0 ]; then echo "$@" echo "${CMD_OUTPUT}" if [ ${ERREXIT} ]; then exit ${ERRCODE} fi fi fi } # Recursively replace @@include@@ template variables with the referenced file, # and write the resulting text to stdout. process_template_includes() { INCSTACK+="$1->" # Includes are relative to the file that does the include. INCDIR=$(dirname $1) # Clear IFS so 'read' doesn't trim whitespace local OLDIFS="$IFS" IFS='' while read -r LINE do INCLINE=$(sed -e '/^[[:space:]]*@@include@@/!d' <<<$LINE) if [ -n "$INCLINE" ]; then INCFILE=$(echo $INCLINE | sed -e "s#@@include@@\(.*\)#\1#") # Simple filename match to detect cyclic includes. CYCLE=$(sed -e "\#$INCFILE#"'!d' <<<$INCSTACK) if [ "$CYCLE" ]; then echo "ERROR: Possible cyclic include detected." 1>&2 echo "$INCSTACK$INCFILE" 1>&2 exit 1 fi if [ ! -r "$INCDIR/$INCFILE" ]; then echo "ERROR: Couldn't read include file: $INCDIR/$INCFILE" 1>&2 exit 1 fi process_template_includes "$INCDIR/$INCFILE" else echo "$LINE" fi done < "$1" IFS="$OLDIFS" INCSTACK=${INCSTACK%"$1->"} } # Replace template variables (@@VARNAME@@) in the given template file. If a # second argument is given, save the processed text to that filename, otherwise # modify the template file in place. process_template() ( # Don't worry if some of these substitution variables aren't set. # Note that this function is run in a sub-shell so we don't leak this # setting, since we still want unbound variables to be an error elsewhere. set +u local TMPLIN="$1" if [ -z "$2" ]; then local TMPLOUT="$TMPLIN" else local TMPLOUT="$2" fi # Process includes first so included text also gets substitutions. TMPLINCL="$(process_template_includes "$TMPLIN")" sed \ -e "s#@@PACKAGE@@#${PACKAGE}#g" \ -e "s#@@PACKAGE_ORIG@@#${PACKAGE_ORIG}#g" \ -e "s#@@PACKAGE_FILENAME@@#${PACKAGE_FILENAME}#g" \ -e "s#@@SNAPNAME@@#${SNAPNAME}#g" \ -e "s#@@PROGNAME@@#${PROGNAME}#g" \ -e "s#@@CHANNEL@@#${CHANNEL}#g" \ -e "s#@@COMPANY_FULLNAME@@#${COMPANY_FULLNAME}#g" \ -e "s#@@VERSION@@#${VERSION}#g" \ -e "s#@@PACKAGE_RELEASE@@#${PACKAGE_RELEASE}#g" \ -e "s#@@VERSIONFULL@@#${VERSIONFULL}#g" \ -e "s#@@INSTALLDIR@@#${INSTALLDIR}#g" \ -e "s#@@BUILDDIR@@#${OUTPUTDIR}#g" \ -e "s#@@STAGEDIR@@#${STAGEDIR}#g" \ -e "s#@@SCRIPTDIR@@#${SCRIPTDIR}#g" \ -e "s#@@ENROLLMENTDIR@@#${ENROLLMENTDIR}#g" \ -e "s#@@MENUNAME@@#${MENUNAME}#g" \ -e "s#@@PRODUCTURL@@#${PRODUCTURL}#g" \ -e "s#@@PREDEPENDS@@#${PREDEPENDS}#g" \ -e "s#@@DEPENDS@@#${DEPENDS}#g" \ -e "s#@@RECOMMENDS@@#${RECOMMENDS}#g" \ -e "s#@@PROVIDES@@#${PROVIDES}#g" \ -e "s#@@ARCHITECTURE@@#${ARCHITECTURE}#g" \ -e "s#@@MAINTNAME@@#${MAINTNAME}#g" \ -e "s#@@MAINTMAIL@@#${MAINTMAIL}#g" \ -e "s#@@REPOCONFIG@@#${REPOCONFIG}#g" \ -e "s#@@REPOCONFIGREGEX@@#${REPOCONFIGREGEX}#g" \ -e "s#@@SHORTDESC@@#${SHORTDESC}#g" \ -e "s#@@FULLDESC@@#${FULLDESC}#g" \ -e "s#@@USR_BIN_SYMLINK_NAME@@#${USR_BIN_SYMLINK_NAME:-}#g" \ -e "s#@@LOGO_RESOURCES_PNG@@#${LOGO_RESOURCES_PNG}#g" \ -e "s#@@LOGO_RESOURCE_XPM@@#${LOGO_RESOURCE_XPM}#g" \ -e "s#@@DATE_RFC5322@@#$(date --rfc-email)#g" \ > "$TMPLOUT" <<< "$TMPLINCL" ) # Setup the installation directory hierarchy in the package staging area. prep_staging_common() { install -m 755 -d "${STAGEDIR}/${INSTALLDIR}" \ "${STAGEDIR}/usr/bin" \ "${STAGEDIR}/usr/share/applications" \ "${STAGEDIR}/usr/share/appdata" \ "${STAGEDIR}/usr/share/gnome-control-center/default-apps" \ "${STAGEDIR}/usr/share/man/man1" } get_version_info() { source "${OUTPUTDIR}/installer/version.txt" VERSION="${MAJOR}.${MINOR}.${BUILD}.${PATCH}" # TODO(phajdan.jr): Provide a mechanism to pass a different package # release number if needed. The meaning of it is to bump it for # packaging-only changes while the underlying software has the same version. # This corresponds to the Release field in RPM spec files and debian_revision # component of the Version field for DEB control file. # Generally with Chrome's fast release cycle it'd be more hassle to try # to bump this number between releases. PACKAGE_RELEASE="1" } stage_install_common() { log_cmd echo "Staging common install files in '${STAGEDIR}'..." # Note: Changes here may also need to be applied to ChromeOS's # chromite/lib/chrome_util.py. # Note: This only supports static binaries and does not work when the GN # is_component_build flag is true. # app STRIPPEDFILE="${OUTPUTDIR}/${PROGNAME}.stripped" install -m 755 "${STRIPPEDFILE}" "${STAGEDIR}/${INSTALLDIR}/${PROGNAME}" # crashpad strippedfile="${OUTPUTDIR}/chrome_crashpad_handler.stripped" install -m 755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/chrome_crashpad_handler" # Final permissions for the chrome-management-service will be set in # postinst chrome_management_service_setup(). strippedfile="${OUTPUTDIR}/chrome_management_service.stripped" install -m 755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/chrome-management-service" # resources install -m 644 "${OUTPUTDIR}/resources.pak" "${STAGEDIR}/${INSTALLDIR}/" # TODO(mmoss): This has broken a couple times on adding new .pak files. Maybe # we should flag all installer files in FILES.cfg and get them from there, so # there's only one place people need to keep track of such things (and in # only the public repository). if [ -r "${OUTPUTDIR}/chrome_100_percent.pak" ]; then install -m 644 "${OUTPUTDIR}/chrome_100_percent.pak" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/chrome_200_percent.pak" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/content_shell.pak" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/shell_resources.pak" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/ui_resources_100_percent.pak" "${STAGEDIR}/${INSTALLDIR}/" install -m 755 "${OUTPUTDIR}/content_shell" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/libffmpeg.so" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/thorium-devtools.png" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/thorium-shell.desktop" "${STAGEDIR}/usr/share/applications/" install -m 755 "${OUTPUTDIR}/thorium-shell" "${STAGEDIR}/usr/bin/" install -m 755 "${OUTPUTDIR}/chromedriver" "${STAGEDIR}/usr/bin/" install -m 755 "${OUTPUTDIR}/pak" "${STAGEDIR}/usr/bin/" else install -m 644 "${OUTPUTDIR}/theme_resources_100_percent.pak" "${STAGEDIR}/${INSTALLDIR}/" install -m 644 "${OUTPUTDIR}/ui_resources_100_percent.pak" "${STAGEDIR}/${INSTALLDIR}/" fi # ICU data file; Necessary when the GN icu_use_data_file flag is true. install -m 644 "${OUTPUTDIR}/icudtl.dat" "${STAGEDIR}/${INSTALLDIR}/" # V8 snapshot files; Necessary when the GN v8_use_external_startup_data flag # is true. # Use v8_context_snapshot.bin instead of snapshot_blob.bin if it is available. # TODO(crbug.com/764576): Unship snapshot_blob.bin on ChromeOS and drop this branch if [ -f "${OUTPUTDIR}/v8_context_snapshot.bin" ]; then install -m 644 "${OUTPUTDIR}/v8_context_snapshot.bin" "${STAGEDIR}/${INSTALLDIR}/" else install -m 644 "${OUTPUTDIR}/snapshot_blob.bin" "${STAGEDIR}/${INSTALLDIR}/" fi # sandbox # Rename sandbox binary with hyphen instead of underscore because that's what # the code looks for. Originally, the SCons build system may have had a bug # where it did not support hyphens, so this is stuck as is to avoid breaking # anyone who expects the build artifact to have the underscore. # the code looks for, but the build targets can't use hyphens (scons bug?) strippedfile="${OUTPUTDIR}/${PROGNAME}_sandbox.stripped" install -m 4755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/chrome-sandbox" # l10n paks install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/locales/" find "${OUTPUTDIR}/locales" -type f -name '*.pak' -exec \ cp -a '{}' "${STAGEDIR}/${INSTALLDIR}/locales/" \; find "${STAGEDIR}/${INSTALLDIR}/locales" -type f -exec chmod 644 '{}' \; # TODO(https://crbug.com/1077934): The below conditions check for the # existence of files to determine if they should be copied to the staging # directory. However, these may be stale if the build config no longer # builds these files. The build config should be obtained from gn rather than # guessed based on the presence of files. # MEI Preload if [ -f "${OUTPUTDIR}/MEIPreload/manifest.json" ]; then install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/MEIPreload/" install -m 644 "${OUTPUTDIR}/MEIPreload/manifest.json" "${STAGEDIR}/${INSTALLDIR}/MEIPreload/" install -m 644 "${OUTPUTDIR}/MEIPreload/preloaded_data.pb" "${STAGEDIR}/${INSTALLDIR}/MEIPreload/" fi # Widevine CDM. if [ -d "${OUTPUTDIR}/WidevineCdm" ]; then # No need to strip; libwidevinecdm.so starts out stripped. cp -a "${OUTPUTDIR}/WidevineCdm" "${STAGEDIR}/${INSTALLDIR}/" find "${STAGEDIR}/${INSTALLDIR}/WidevineCdm" -type d -exec chmod 755 '{}' \; find "${STAGEDIR}/${INSTALLDIR}/WidevineCdm" -type f -exec chmod 644 '{}' \; find "${STAGEDIR}/${INSTALLDIR}/WidevineCdm" -name libwidevinecdm.so \ -exec chmod ${SHLIB_PERMS} '{}' \; fi # ANGLE if [ -f "${OUTPUTDIR}/libEGL.so" ]; then for file in libEGL.so libGLESv2.so; do strippedfile="${OUTPUTDIR}/${file}.stripped" install -m ${SHLIB_PERMS} "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}" done fi # ANGLE's libvulkan library if [ -f "${OUTPUTDIR}/libvulkan.so.1" ]; then file="libvulkan.so.1" strippedfile="${OUTPUTDIR}/${file}.stripped" install -m 755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}" fi # SwiftShader VK if [ -f "${OUTPUTDIR}/libvk_swiftshader.so" ]; then install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/" file="libvk_swiftshader.so" strippedfile="${OUTPUTDIR}/${file}.stripped" install -m ${SHLIB_PERMS} "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}" # Install the ICD json file to point ANGLE to libvk_swiftshader.so install -m 644 "${OUTPUTDIR}/vk_swiftshader_icd.json" "${STAGEDIR}/${INSTALLDIR}/" fi # Optimization Guide Internal if [ -f "${OUTPUTDIR}/liboptimization_guide_internal.so" ]; then file="liboptimization_guide_internal.so" strippedfile="${OUTPUTDIR}/${file}.stripped" install -m ${SHLIB_PERMS} "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}" fi # libc++ if [ -f "${OUTPUTDIR}/lib/libc++.so" ]; then install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/lib/" install -m ${SHLIB_PERMS} -s "${OUTPUTDIR}/lib/libc++.so" "${STAGEDIR}/${INSTALLDIR}/lib/" fi # nacl_helper and nacl_helper_bootstrap # Don't use "-s" (strip) because this runs binutils "strip", which # mangles the special ELF program headers of nacl_helper_bootstrap. # Explicitly use eu-strip instead, because it doesn't have that problem. for file in nacl_helper nacl_helper_bootstrap; do buildfile="${OUTPUTDIR}/${file}" if [ -f "${buildfile}" ]; then strippedfile="${buildfile}.stripped" install -m 755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}" fi done # Don't use "-s" (strip) because this would use the Linux toolchain to # strip the NaCl binary, which has the potential to break it. It # certainly resets the OSABI and ABIVERSION fields to non-NaCl values, # although the NaCl IRT loader doesn't care about these fields. In any # case, the IRT binaries are already stripped by NaCl's build process. for filename in ${OUTPUTDIR}/nacl_irt_*.nexe; do # Re-check the filename in case globbing matched nothing. if [ -f "$filename" ]; then install -m 644 "$filename" "${STAGEDIR}/${INSTALLDIR}/`basename "$filename"`" fi done # default apps if [ -d "${OUTPUTDIR}/default_apps" ]; then cp -a "${OUTPUTDIR}/default_apps" "${STAGEDIR}/${INSTALLDIR}/" find "${STAGEDIR}/${INSTALLDIR}/default_apps" -type d -exec chmod 755 '{}' \; find "${STAGEDIR}/${INSTALLDIR}/default_apps" -type f -exec chmod 644 '{}' \; fi # launcher script and symlink process_template "${OUTPUTDIR}/installer/common/wrapper" \ "${STAGEDIR}/${INSTALLDIR}/${PACKAGE}" chmod 755 "${STAGEDIR}/${INSTALLDIR}/${PACKAGE}" if [ ! -z "${PACKAGE_ORIG}" ]; then if [ ! -f "${STAGEDIR}/${INSTALLDIR}/${PACKAGE_ORIG}" ]; then ln -sn "${INSTALLDIR}/${PACKAGE}" \ "${STAGEDIR}/${INSTALLDIR}/${PACKAGE_ORIG}" fi fi if [ ! -z "${USR_BIN_SYMLINK_NAME}" ]; then ln -snf "${INSTALLDIR}/${PACKAGE}" \ "${STAGEDIR}/usr/bin/${USR_BIN_SYMLINK_NAME}" fi # app icons local icon_regex=".*product_logo_[0-9]\+\." if [ "$BRANDING" = "google_chrome" ]; then if [ "$CHANNEL" = "beta" ]; then icon_regex=".*product_logo_[0-9]\+_beta\." elif [ "$CHANNEL" = "unstable" ]; then icon_regex=".*product_logo_[0-9]\+_dev\." fi fi LOGO_RESOURCES_PNG=$(find "${OUTPUTDIR}/installer/theme/" \ -regextype sed -regex "${icon_regex}png" -printf "%f ") LOGO_RESOURCE_XPM=$(find "${OUTPUTDIR}/installer/theme/" \ -regextype sed -regex "${icon_regex}xpm" -printf "%f") for logo in ${LOGO_RESOURCES_PNG} ${LOGO_RESOURCE_XPM}; do install -m 644 \ "${OUTPUTDIR}/installer/theme/${logo}" \ "${STAGEDIR}/${INSTALLDIR}/" done # desktop integration install -m 755 "${OUTPUTDIR}/xdg-mime" "${STAGEDIR}${INSTALLDIR}/" install -m 755 "${OUTPUTDIR}/xdg-settings" "${STAGEDIR}${INSTALLDIR}/" if [ ${PACKAGE:0:6} = google ]; then process_template "${OUTPUTDIR}/installer/common/google-chrome.appdata.xml.template" \ "${STAGEDIR}/usr/share/appdata/${PACKAGE}.appdata.xml" chmod 644 "${STAGEDIR}/usr/share/appdata/${PACKAGE}.appdata.xml" else install -m 644 "${OUTPUTDIR}/installer/common/chromium-browser.appdata.xml" \ "${STAGEDIR}/usr/share/appdata/${PACKAGE}.appdata.xml" fi process_template "${OUTPUTDIR}/installer/common/desktop.template" \ "${STAGEDIR}/usr/share/applications/${PACKAGE}.desktop" chmod 644 "${STAGEDIR}/usr/share/applications/${PACKAGE}.desktop" process_template "${OUTPUTDIR}/installer/common/default-app.template" \ "${STAGEDIR}/usr/share/gnome-control-center/default-apps/${PACKAGE}.xml" chmod 644 "${STAGEDIR}/usr/share/gnome-control-center/default-apps/${PACKAGE}.xml" process_template "${OUTPUTDIR}/installer/common/default-app-block.template" \ "${STAGEDIR}${INSTALLDIR}/default-app-block" chmod 644 "${STAGEDIR}${INSTALLDIR}/default-app-block" # documentation process_template "${OUTPUTDIR}/installer/common/manpage.1.in" \ "${STAGEDIR}/usr/share/man/man1/${USR_BIN_SYMLINK_NAME}.1" gzip -9n "${STAGEDIR}/usr/share/man/man1/${USR_BIN_SYMLINK_NAME}.1" chmod 644 "${STAGEDIR}/usr/share/man/man1/${USR_BIN_SYMLINK_NAME}.1.gz" # The stable channel allows launching the app without the "-stable" # suffix like the other channels. Create a linked man page for the # app-without-the-channel case. if [ ! -f "${STAGEDIR}/usr/share/man/man1/${PACKAGE}.1.gz" ]; then ln -s "${USR_BIN_SYMLINK_NAME}.1.gz" \ "${STAGEDIR}/usr/share/man/man1/${PACKAGE}.1.gz" fi # Check to make sure all the ELF binaries are stripped. UNSTRIPPED=$(find "${STAGEDIR}/${INSTALLDIR}/" -type f | xargs file | grep ELF | grep -c "not stripped" || true) if [ "${UNSTRIPPED}" != "0" ]; then echo "NOTICE: Found ${UNSTRIPPED} unstripped ELF files." 1>&2 fi # Check to make sure no ELF binaries set RPATH. if [ "${TARGET_OS}" != "chromeos" ]; then RPATH_BINS= for elf in $(find "${STAGEDIR}/${INSTALLDIR}/" -type f | xargs file | grep ELF | awk '{print $1;}' | sed 's/:$//'); do if readelf -d ${elf} | grep "(RPATH)" >/dev/null; then RPATH_BINS="${RPATH_BINS} $(basename ${elf})" fi done if [ -n "${RPATH_BINS}" ]; then echo "NOTICE: Found binaries with RPATH set:${RPATH_BINS}" 1>&2 fi fi # Make sure ELF binaries live in INSTALLDIR exclusively. ELF_OUTSIDE_INSTALLDIR=$(find "${STAGEDIR}/" -not -path \ "${STAGEDIR}${INSTALLDIR}/*" -type f | xargs file -b | grep -ce "^ELF" || true) if [ "${ELF_OUTSIDE_INSTALLDIR}" -ne 0 ]; then echo "NOTICE: Found ${ELF_OUTSIDE_INSTALLDIR} ELF binaries" \ "outside of ${INSTALLDIR}" 1>&2 fi # Verify file permissions. for file in $(find "${STAGEDIR}" -mindepth 1); do local actual_perms=$(stat -c "%a" "${file}") local file_type="$(file -b "${file}")" local base_name=$(basename "${file}") if [[ "${file_type}" = "directory"* ]]; then local expected_perms=755 elif [[ "${file_type}" = *"symbolic link"* ]]; then if [[ "$(readlink ${file})" = "/"* ]]; then # Absolute symlink. local expect_exists="${STAGEDIR}/$(readlink "${file}")" else # Relative symlink. local expect_exists="$(dirname "${file}")/$(readlink "${file}")" fi if [ ! -f "${expect_exists}" ]; then echo "Broken symlink: ${file}" 1>&2 exit 1 fi local expected_perms=777 elif [ "${base_name}" = "chrome-management-service" ]; then local expected_perms=755 elif [ "${base_name}" = "chrome-sandbox" ]; then local expected_perms=4755 elif [[ "${base_name}" = "nacl_irt_"*".nexe" ]]; then local expected_perms=644 elif [[ "${file_type}" = *"shell script"* ]]; then local expected_perms=755 elif [[ "${file_type}" = ELF* ]]; then if [[ "${base_name}" = *".so" ]]; then local expected_perms=${SHLIB_PERMS} else local expected_perms=755 fi else # Regular data file. local expected_perms=644 fi if [ ${expected_perms} -ne ${actual_perms} ]; then echo Expected permissions on ${base_name} to be \ ${expected_perms}, but they were ${actual_perms} 1>&2 exit 1 fi done }