| # 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() { |
| local TMPLIN="$1" |
| local TMPLOUT="${2:-$TMPLIN}" |
| |
| # Process includes first |
| local TMPLINCL="$(process_template_includes "$TMPLIN")" |
| |
| # Substitute templated variables from the environment. |
| local sed_args="" |
| for varname in $(comm -2 <(compgen -v | sort) \ |
| <(env -0 | cut -z -f1 -d= | tr '\0' '\n' | sort)); do |
| if [[ "$TMPLINCL" == *"@@${varname}@@"* ]]; then |
| val=$(printf '%s' "${!varname}" | sed 's/[#\/&]/\\&/g') |
| sed_args="${sed_args}s#@@${varname}@@#${val}#g;" |
| fi |
| done |
| echo "$TMPLINCL" | sed "$sed_args" > "$TMPLOUT" |
| |
| # Verify no placeholders remain |
| if grep -q '@@' "$TMPLOUT"; then |
| echo "Error: Some placeholders remain unfilled in $TMPLOUT:" >&2 |
| grep '@@' "$TMPLOUT" >&2 |
| exit 1 |
| fi |
| } |
| |
| # 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}/" |
| 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/40539769): 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(crbug.com/40688962): 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. |
| |
| # Privacy Sandbox Attestation |
| if [ -f "${OUTPUTDIR}/PrivacySandboxAttestationsPreloaded/manifest.json" ]; then |
| install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/PrivacySandboxAttestationsPreloaded/" |
| install -m 644 "${OUTPUTDIR}/PrivacySandboxAttestationsPreloaded/manifest.json" "${STAGEDIR}/${INSTALLDIR}/PrivacySandboxAttestationsPreloaded/" |
| install -m 644 "${OUTPUTDIR}/PrivacySandboxAttestationsPreloaded/privacy-sandbox-attestations.dat" "${STAGEDIR}/${INSTALLDIR}/PrivacySandboxAttestationsPreloaded/" |
| fi |
| |
| # 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 ${SHLIB_PERMS} "${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 |
| |
| # QT shim |
| if [ -f "${OUTPUTDIR}/libqt5_shim.so" ]; then |
| file="libqt5_shim.so" |
| strippedfile="${OUTPUTDIR}/${file}.stripped" |
| install -m ${SHLIB_PERMS} "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}" |
| fi |
| if [ -f "${OUTPUTDIR}/libqt6_shim.so" ]; then |
| file="libqt6_shim.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 |
| |
| # 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 |
| |
| # CHROME_VERSION_EXTRA file containing the channel |
| if [ "$BRANDING" = "google_chrome" ]; then |
| echo "$CHANNEL" > "${STAGEDIR}/${INSTALLDIR}/CHROME_VERSION_EXTRA" |
| chmod 644 "${STAGEDIR}${INSTALLDIR}/CHROME_VERSION_EXTRA" |
| 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_suffix="" |
| if [ "$BRANDING" = "google_chrome" ]; then |
| if [ "$CHANNEL" = "beta" ]; then |
| icon_suffix="_beta" |
| elif [ "$CHANNEL" = "unstable" ]; then |
| icon_suffix="_dev" |
| elif [ "$CHANNEL" = "canary" ]; then |
| icon_suffix="_canary" |
| fi |
| fi |
| local icon_sizes="16 24 32 48 64 128 256" |
| LOGO_RESOURCES_PNG="" |
| for size in ${icon_sizes}; do |
| local logo="product_logo_${size}${icon_suffix}.png" |
| LOGO_RESOURCES_PNG="${LOGO_RESOURCES_PNG} ${logo}" |
| 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}/" |
| |
| process_template "${OUTPUTDIR}/installer/common/appdata.xml.template" \ |
| "${STAGEDIR}/usr/share/appdata/${PACKAGE}.appdata.xml" |
| chmod 644 "${STAGEDIR}/usr/share/appdata/${PACKAGE}.appdata.xml" |
| |
| process_template "${OUTPUTDIR}/installer/common/desktop.template" \ |
| "${STAGEDIR}/usr/share/applications/${PACKAGE}.desktop" |
| chmod 644 "${STAGEDIR}/usr/share/applications/${PACKAGE}.desktop" |
| cp "${STAGEDIR}/usr/share/applications/${PACKAGE}.desktop" \ |
| "${STAGEDIR}/usr/share/applications/${RDN_DESKTOP}.desktop" |
| chmod 644 "${STAGEDIR}/usr/share/applications/${RDN_DESKTOP}.desktop" |
| sed -i "/^\[Desktop Entry\]$/a \ |
| # This is the same as ${PACKAGE}.desktop except NoDisplay=true prevents\n\ |
| # duplicate menu entries. This is required to match the application ID\n\ |
| # used by XDG desktop portal, which has stricter naming requirements.\n\ |
| # The old desktop file is kept to preserve default browser settings.\n\ |
| NoDisplay=true" "${STAGEDIR}/usr/share/applications/${RDN_DESKTOP}.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_BINS= |
| for elf in $(find "${STAGEDIR}/${INSTALLDIR}/" -type f | xargs file | |
| grep ELF | grep "not stripped" | awk '{print $1;}' | |
| sed 's/:$//'); do |
| UNSTRIPPED_BINS="${UNSTRIPPED_BINS} ${elf}" |
| done |
| |
| if [ -n "${UNSTRIPPED_BINS}" ]; then |
| echo "ERROR: Found unstripped ELF files:${UNSTRIPPED_BINS}" 1>&2 |
| exit 1 |
| 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 "ERROR: Found binaries with RPATH set:${RPATH_BINS}" 1>&2 |
| exit 1 |
| 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 "ERROR: Found ${ELF_OUTSIDE_INSTALLDIR} ELF binaries" \ |
| "outside of ${INSTALLDIR}" 1>&2 |
| exit 1 |
| fi |
| |
| # official build should not relax permission check. |
| on_cog= |
| if [ "$IS_OFFICIAL_BUILD" -eq 0 ]; then |
| # on Cog, permission is always 0664 or 0775 |
| case "$(pwd)" in |
| /google/cog/cloud/*) |
| on_cog=1 |
| echo "INFO: build on Cog. relax permission for group writable" 1>&2 |
| ;; |
| esac |
| 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 [[ "${file_type}" = *"shell script"* ]]; then |
| local expected_perms=755 |
| elif [[ "${file_type}" = ELF* ]]; then |
| if [[ "${base_name}" = *".so" || "${base_name}" = *".so."[[:digit:]]* ]]; 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 |
| if [ -z "$on_cog" ]; then |
| echo Expected permissions on ${base_name} to be \ |
| ${expected_perms}, but they were ${actual_perms} 1>&2 |
| exit 1 |
| fi |
| local relaxed_expected_perms=${expected_perms} |
| |
| case ${expected_perms} in |
| 4755) relaxed_expected_perms=775 ;; |
| 644) relaxed_expected_perms=664 ;; |
| 755) relaxed_expected_perms=775 ;; |
| esac |
| |
| if [ ${relaxed_expected_perms} -ne ${actual_perms} ]; then |
| echo Expected permissions on ${base_name} to be \ |
| ${expected_perms} or ${relaxed_expected_perms}, \ |
| but they were ${actual_perms} 1>&2 |
| exit 1 |
| fi |
| fi |
| done |
| } |