diff options
author | thomasvl@chromium.org <thomasvl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-24 16:43:04 +0000 |
---|---|---|
committer | thomasvl@chromium.org <thomasvl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-24 16:43:04 +0000 |
commit | a2126d5e54f773947d5a400f0caec33f4ea1d930 (patch) | |
tree | 81d026b3318923cae14fe25f6fb539f90e672a18 /chrome | |
parent | 90a99dc148bd74520db74fbc80fa3d8639e2d40f (diff) | |
download | chromium_src-a2126d5e54f773947d5a400f0caec33f4ea1d930.zip chromium_src-a2126d5e54f773947d5a400f0caec33f4ea1d930.tar.gz chromium_src-a2126d5e54f773947d5a400f0caec33f4ea1d930.tar.bz2 |
Tweak the comment in the lproj fixup script to explain why we need an "en" folder.
Added a string for the products short name, this is current mac only, and is used for the application menu title via the infoplist.strings file.
Added source for a tool to build InfoPlist.strings files based on the values within the GRD files.
Run the InfoPlist.strings generation tool during the build.
Added a script to take a string and list of locale and generate all the versions of the string.
Wired up the string locale tool so GYP knows about all the inputs/outputs from the InfoPlist.strings generation tool.
Stop setting some of the Info.plist keys that are now covered by the InfoPlist.strings files.
Update the mac links script to stop creating a resources link.
Add a shim to nuke the helper's resource link so it can get a real folder.
Helper in in chrome_paths_mac to find the main app's bundle (that the helper lives in).
At startup, if not the browser, set the main bundle to be the parent of this bundle.
Fix up the breakpad init to use the mac_util helper for main bundle.
TEST=when runnining in the supported languages, Finder Get Info should show a localized copyright. The process names in activity monitor should also be correct and show localized names once the TC work is done.
BUG=19019
Review URL: http://codereview.chromium.org/171040
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24104 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/app-Info.plist | 50 | ||||
-rw-r--r-- | chrome/app/breakpad_mac.mm | 3 | ||||
-rw-r--r-- | chrome/app/chrome_dll_main.cc | 15 | ||||
-rw-r--r-- | chrome/app/chromium_strings.grd | 11 | ||||
-rw-r--r-- | chrome/app/google_chrome_strings.grd | 11 | ||||
-rw-r--r-- | chrome/app/helper-Info.plist | 6 | ||||
-rwxr-xr-x | chrome/app/make_mac_app_symlinks | 9 | ||||
-rwxr-xr-x | chrome/app/nuke_mac_resources_link | 19 | ||||
-rwxr-xr-x | chrome/app/tweak_mac_lproj_folders | 11 | ||||
-rw-r--r-- | chrome/chrome.gyp | 122 | ||||
-rw-r--r-- | chrome/common/chrome_paths_internal.h | 9 | ||||
-rw-r--r-- | chrome/common/chrome_paths_mac.mm | 27 | ||||
-rw-r--r-- | chrome/tools/build/apply_locales.py | 27 | ||||
-rw-r--r-- | chrome/tools/mac_helpers/infoplist_strings_util.mm | 296 |
14 files changed, 580 insertions, 36 deletions
diff --git a/chrome/app/app-Info.plist b/chrome/app/app-Info.plist index 1341cdc..1e65fda 100644 --- a/chrome/app/app-Info.plist +++ b/chrome/app/app-Info.plist @@ -3,27 +3,9 @@ <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>UTExportedTypeDeclarations</key> - <array> - <dict> - <key>UTTypeIdentifier</key> - <string>org.chromium.extension</string> - <key>UTTypeDescription</key> - <string>Chromium Extra</string> - <key>UTTypeConformsTo</key> - <array> - <string>public.data</string> - </array> - <key>UTTypeTagSpecification</key> - <dict> - <key>public.filename-extension</key> - <array> - <string>crx</string> - </array> - </dict> - </dict> - </array> + <string>en-US</string> + <key>CFBundleDisplayName</key> + <string>${EXECUTABLE_NAME}</string> <key>CFBundleDocumentTypes</key> <array> <dict> @@ -134,12 +116,12 @@ <string>Viewer</string> </dict> <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> <key>LSItemContentTypes</key> <array> <string>org.chromium.extension</string> </array> - <key>CFBundleTypeRole</key> - <string>Viewer</string> </dict> </array> <key>CFBundleExecutable</key> @@ -188,11 +170,33 @@ <string>0.1</string> <key>LSFileQuarantineEnabled</key> <true/> + <key>LSHasLocalizedDisplayName</key> + <string>1</string> <key>LSMinimumSystemVersion</key> <string>10.5.0</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> <string>NSApplication</string> + <key>UTExportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>Chromium Extra</string> + <key>UTTypeIdentifier</key> + <string>org.chromium.extension</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>crx</string> + </array> + </dict> + </dict> + </array> </dict> </plist> diff --git a/chrome/app/breakpad_mac.mm b/chrome/app/breakpad_mac.mm index 142a46e..6acb3f4 100644 --- a/chrome/app/breakpad_mac.mm +++ b/chrome/app/breakpad_mac.mm @@ -10,6 +10,7 @@ #import "base/basictypes.h" #include "base/command_line.h" #import "base/logging.h" +#include "base/mac_util.h" #import "base/scoped_nsautorelease_pool.h" #include "base/sys_string_conversions.h" #import "breakpad/src/client/mac/Framework/Breakpad.h" @@ -44,7 +45,7 @@ void InitCrashReporter() { // may not have access to the disk or to the same data as the browser // process, so the browser passes the consent preference to them on the // command line. - NSBundle* main_bundle = [NSBundle mainBundle]; + NSBundle* main_bundle = mac_util::MainAppBundle(); NSDictionary* info_dictionary = [main_bundle infoDictionary]; bool is_browser = [[info_dictionary objectForKey:@"LSUIElement"] isEqualToString:@"1"] ? false : true; diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index d2af7a9..64f5ce53 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -49,6 +49,8 @@ #include "base/win_util.h" #endif #if defined(OS_MACOSX) +#include "base/mac_util.h" +#include "chrome/common/chrome_paths_internal.h" #include "chrome/app/breakpad_mac.h" #endif #if defined(OS_LINUX) @@ -335,6 +337,17 @@ int ChromeMain(int argc, const char** argv) { #endif const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); + std::wstring process_type = + parsed_command_line.GetSwitchValue(switches::kProcessType); + +#if defined(OS_MACOSX) + // If process_type is not empty, this is the helper. Set the main app bundle + // so code can fetch Mac resources. + if (!process_type.empty()) { + FilePath main_path(chrome::GetBrowserBundlePath()); + mac_util::SetOverrideAppBundlePath(main_path); + } +#endif // OS_MACOSX #if defined(OS_WIN) // Must do this before any other usage of command line! @@ -373,8 +386,6 @@ int ChromeMain(int argc, const char** argv) { #endif // OS_POSIX int browser_pid; - std::wstring process_type = - parsed_command_line.GetSwitchValue(switches::kProcessType); if (process_type.empty()) { browser_pid = base::GetCurrentProcId(); } else { diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd index 2127282..b61d19a 100644 --- a/chrome/app/chromium_strings.grd +++ b/chrome/app/chromium_strings.grd @@ -349,6 +349,17 @@ be available for now. --> <message name="IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT" desc="More compact text to show in the default browser query infobar."> Chromium isn't your default browser. </message> + <if expr="os == 'darwin'"> + <message name="IDS_SHORT_PRODUCT_NAME" desc="The application's short name, used for the Mac's application menu, activity monitor, etc. This should be less than 16 characters. Example: Chrome, not Google Chrome."> + Chromium + </message> + <message name="IDS_HELPER_NAME" desc="The helper application's name. Should contain the Chrome application name (IDS_PRODUCT_NAME). Example: Google Chrome Helper."> + Chromium Helper + </message> + <message name="IDS_SHORT_HELPER_NAME" desc="The helper application's short name, used for the Mac's application menu, activity monitor, etc. Example: Chrome Helper, not Google Chrome Helper."> + Chromium Helper + </message> + </if> </messages> </release> </grit> diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd index bec59f6..5548e28 100644 --- a/chrome/app/google_chrome_strings.grd +++ b/chrome/app/google_chrome_strings.grd @@ -397,6 +397,17 @@ Chrome supports. --> <message name="IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT" desc="More compact text to show in the default browser query infobar."> Google Chrome isn't your default browser. </message> + <if expr="os == 'darwin'"> + <message name="IDS_SHORT_PRODUCT_NAME" desc="The application's short name, used for the Mac's application menu, activity monitor, etc. This should be less than 16 characters. Example: Chrome, not Google Chrome."> + Google Chrome + </message> + <message name="IDS_HELPER_NAME" desc="The helper application's name. Should contain the Chrome application name (IDS_PRODUCT_NAME). Example: Google Chrome Helper."> + Google Chrome Helper + </message> + <message name="IDS_SHORT_HELPER_NAME" desc="The helper application's short name, used for the Mac's application menu, activity monitor, etc. Example: Chrome Helper, not Google Chrome Helper."> + Chrome Helper + </message> + </if> </messages> </release> </grit> diff --git a/chrome/app/helper-Info.plist b/chrome/app/helper-Info.plist index 9aeacff..72f92b7 100644 --- a/chrome/app/helper-Info.plist +++ b/chrome/app/helper-Info.plist @@ -3,7 +3,9 @@ <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> - <string>English</string> + <string>en-US</string> + <key>CFBundleDisplayName</key> + <string>${EXECUTABLE_NAME}</string> <key>CFBundleExecutable</key> <string>${EXECUTABLE_NAME}</string> <key>CFBundleIdentifier</key> @@ -20,6 +22,8 @@ <string>0.1</string> <key>LSFileQuarantineEnabled</key> <true/> + <key>LSHasLocalizedDisplayName</key> + <string>1</string> <key>LSMinimumSystemVersion</key> <string>10.5.0</string> <key>LSUIElement</key> diff --git a/chrome/app/make_mac_app_symlinks b/chrome/app/make_mac_app_symlinks index 5f3c040..66353a95 100755 --- a/chrome/app/make_mac_app_symlinks +++ b/chrome/app/make_mac_app_symlinks @@ -5,14 +5,13 @@ # found in the LICENSE file. # This script is intended to run from a "postbuild" action from within Xcode. -# It sets up symbolic links for an app bundle's Resources and Frameworks -# directories. This is intended to be used for app bundles that live within -# the Resources directory of a larger app bundle, when the sub-app's -# Resources and Frameworks directories should point to the enclosing app's. +# It sets up symbolic links for an app bundle's Frameworks directory. This is +# intended to be used for app bundles that live within the Resources directory +# of a larger app bundle, when the sub-app's Frameworks directory should point +# to the enclosing app's. set -e CONTENTS_PATH="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Contents" -ln -fhs ../.. "${CONTENTS_PATH}/Resources" ln -fhs ../../../Frameworks "${CONTENTS_PATH}/Frameworks" diff --git a/chrome/app/nuke_mac_resources_link b/chrome/app/nuke_mac_resources_link new file mode 100755 index 0000000..98c5c75 --- /dev/null +++ b/chrome/app/nuke_mac_resources_link @@ -0,0 +1,19 @@ +#!/bin/sh + +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# TODO: Remove this script and the action that invokes it after 09/01/09 + +# The helper's Resources directory used to be a link, but now that we need +# real resources, we have this script deletes the link as a stop gap for the +# build bots and developers' builds. + +set -e + +RESOURCES_PATH="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Contents/Resources" + +if [ -L "${RESOURCES_PATH}" ] ; then + rm -f "${RESOURCES_PATH}" +fi diff --git a/chrome/app/tweak_mac_lproj_folders b/chrome/app/tweak_mac_lproj_folders index d15c858..78dba5b 100755 --- a/chrome/app/tweak_mac_lproj_folders +++ b/chrome/app/tweak_mac_lproj_folders @@ -19,4 +19,15 @@ cd "${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Contents/Resources" rm -rf "zh.lproj" "en.lproj" # Provide an link for en.lproj that points to en-US.lproj. + +# The standard OS language list includes English (en) and not a specific +# "flavor" such as U.S. English (en-US). When checking for a language match, +# the OS will remove subtags if no exact match is found, so that en will be +# used if the user's preferred language list only contains British English +# (en-GB) and no specific en-GB translation is available. +# +# To ensure that users with the default language list, which contains English +# without any regional subtag, as well as users with a specific variant of +# English with a regional subtag, will have access to an English-localized +# application, a symbolic link from en.lproj to en-US.lproj is provided. ln -fhs "en-US.lproj" "en.lproj" diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 69d534c..1b59fd8 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -3144,6 +3144,7 @@ # chrome/app/app-Info.plist has: # CFBundleIdentifier of CHROMIUM_BUNDLE_ID # CFBundleName of CHROMIUM_SHORT_NAME + # CFBundleSignature of CHROMIUM_CREATOR # Xcode then replaces these values with the branded values we set # as settings on the target. 'CHROMIUM_BUNDLE_ID': '<(mac_bundle_id)', @@ -3155,6 +3156,7 @@ ], 'dependencies': [ 'helper_app', + 'infoplist_strings_tool', # Bring in pdfsqueeze and run it on all pdfs '../build/temp_gyp/pdfsqueeze.gyp:pdfsqueeze', ], @@ -3172,6 +3174,51 @@ 'message': 'Running pdfsqueeze on <(RULE_INPUT_PATH)', }, ], + 'actions': [ + { + # Generate the InfoPlist.strings file + 'action_name': 'Generating InfoPlist.strings files', + 'variables': { + 'tool_path': '<(PRODUCT_DIR)/infoplist_strings_tool', + 'version_file_path': 'VERSION', + # Unique dir to write to so the [lang].lproj/InfoPlist.strings + # for the main app and the helper app don't name collide. + 'output_path': '<(INTERMEDIATE_DIR)/app_infoplist_strings', + }, + 'conditions': [ + [ 'branding == "Chrome"', { + 'variables': { + 'branding_name': 'google_chrome_strings', + }, + }, { # else branding!="Chrome" + 'variables': { + 'branding_name': 'chromium_strings', + }, + }], + ], + 'inputs': [ + '<(tool_path)', + '<(version_file_path)', + # TODO: remove this helper when we have loops in GYP + '>!@(tools/build/apply_locales.py \'<(grit_out_dir)/<(branding_name)_ZZLOCALE.pak\' <(locales))', + ], + 'outputs': [ + # TODO: remove this helper when we have loops in GYP + '>!@(tools/build/apply_locales.py \'<(output_path)/ZZLOCALE.lproj/InfoPlist.strings\' <(locales))', + ], + 'action': [ + '<(tool_path)', + '-b', '<(branding_name)', + '-v', '<(version_file_path)', + '-g', '<(grit_out_dir)', + '-o', '<(output_path)', + '-t', 'main', + '<@(locales)', + ], + 'message': 'Generating the language InfoPlist.strings files', + 'process_outputs_as_mac_bundle_resources': 1, + }, + ], 'copies': [ { 'destination': '<(PRODUCT_DIR)/<(mac_product_name).app/Contents/Frameworks', @@ -4692,6 +4739,7 @@ 'dependencies': [ 'chrome_dll', 'interpose_dependency_shim', + 'infoplist_strings_tool', ], 'sources': [ # chrome_exe_main.mm's main() is the entry point for the "chrome" @@ -4729,6 +4777,58 @@ ], }, ], + 'actions': [ + { + # TODO: remove this action and the script it runs after 09/01/09 + 'action_name': 'Remove old resources symlink', + 'inputs': [], + 'outputs': [], + 'action': [ 'app/nuke_mac_resources_link' ], + }, + { + # Generate the InfoPlist.strings file + 'action_name': 'Generating InfoPlist.strings files', + 'variables': { + 'tool_path': '<(PRODUCT_DIR)/infoplist_strings_tool', + 'version_file_path': 'VERSION', + # Unique dir to write to so the [lang].lproj/InfoPlist.strings + # for the main app and the helper app don't name collide. + 'output_path': '<(INTERMEDIATE_DIR)/helper_infoplist_strings', + }, + 'conditions': [ + [ 'branding == "Chrome"', { + 'variables': { + 'branding_name': 'google_chrome_strings', + }, + }, { # else branding!="Chrome" + 'variables': { + 'branding_name': 'chromium_strings', + }, + }], + ], + 'inputs': [ + '<(tool_path)', + '<(version_file_path)', + # TODO: remove this helper when we have loops in GYP + '>!@(tools/build/apply_locales.py \'<(grit_out_dir)/<(branding_name)_ZZLOCALE.pak\' <(locales))', + ], + 'outputs': [ + # TODO: remove this helper when we have loops in GYP + '>!@(tools/build/apply_locales.py \'<(output_path)/ZZLOCALE.lproj/InfoPlist.strings\' <(locales))', + ], + 'action': [ + '<(tool_path)', + '-b', '<(branding_name)', + '-v', '<(version_file_path)', + '-g', '<(grit_out_dir)', + '-o', '<(output_path)', + '-t', 'helper', + '<@(locales)', + ], + 'message': 'Generating the language InfoPlist.strings files', + 'process_outputs_as_mac_bundle_resources': 1, + }, + ], 'postbuilds': [ { 'postbuild_name': 'Make Symbolic Links', @@ -4748,6 +4848,10 @@ '-s0', '<(branding)'], }, + { + 'postbuild_name': 'Tweak Mac lproj folders', + 'action': ['app/tweak_mac_lproj_folders'], + }, ], 'conditions': [ ['mac_breakpad==1', { @@ -4831,7 +4935,21 @@ 'DYLIB_INSTALL_NAME_BASE': '@executable_path', }, }, - ] + { + 'target_name': 'infoplist_strings_tool', + 'type': 'executable', + 'dependencies': [ + 'chrome_strings', + '../base/base.gyp:base', + ], + 'include_dirs': [ + '<(grit_out_dir)', + ], + 'sources': [ + 'tools/mac_helpers/infoplist_strings_util.mm', + ], + }, + ], # targets }, { # else: OS != "mac" 'targets': [ { @@ -5139,7 +5257,7 @@ }], ], }, - ] + ], }], ['OS=="win"', { 'targets': [ diff --git a/chrome/common/chrome_paths_internal.h b/chrome/common/chrome_paths_internal.h index f1f0fa9..54b9870 100644 --- a/chrome/common/chrome_paths_internal.h +++ b/chrome/common/chrome_paths_internal.h @@ -7,7 +7,7 @@ #include "build/build_config.h" -class FilePath; +#include "base/file_path.h" namespace chrome { @@ -29,6 +29,13 @@ bool GetUserDownloadsDirectory(FilePath* result); // The path to the user's desktop. bool GetUserDesktop(FilePath* result); +#if defined(OS_MACOSX) +// Retrieves the browser bundle path. It is only valid to call this from a +// helper process, as it makes assumptions about the location of the enclosing +// bundle on disk. +FilePath GetBrowserBundlePath(); +#endif // defined(OS_MACOSX) + } // namespace chrome diff --git a/chrome/common/chrome_paths_mac.mm b/chrome/common/chrome_paths_mac.mm index 618dd4f..82dbb3c 100644 --- a/chrome/common/chrome_paths_mac.mm +++ b/chrome/common/chrome_paths_mac.mm @@ -7,7 +7,6 @@ #import <Cocoa/Cocoa.h> #import "base/base_paths.h" -#import "base/file_path.h" #import "base/logging.h" #import "base/path_service.h" @@ -72,4 +71,30 @@ bool GetUserDesktop(FilePath* result) { return success; } +FilePath GetBrowserBundlePath() { + NSBundle* running_app_bundle = [NSBundle mainBundle]; + NSString* running_app_bundle_path = [running_app_bundle bundlePath]; + DCHECK(running_app_bundle_path) << "failed to get the main bundle path"; + + // Are we the helper or the browser (main bundle)? + if (![[[running_app_bundle infoDictionary] + objectForKey:@"LSUIElement"] boolValue]) { + // We aren't a LSUIElement, so this must be the browser, return it's path. + return FilePath([running_app_bundle_path fileSystemRepresentation]); + } + + // Helper lives at ...app/Contents/Resources/...Helper.app + NSArray* components = [running_app_bundle_path pathComponents]; + DCHECK_GE([components count], static_cast<NSUInteger>(4)) + << "too few path components for this bundle to be within another bundle"; + components = + [components subarrayWithRange:NSMakeRange(0, [components count] - 3)]; + + NSString* browser_path = [NSString pathWithComponents:components]; + DCHECK([[browser_path pathExtension] isEqualToString:@"app"]) + << "we weren't within another app?"; + + return FilePath([browser_path fileSystemRepresentation]); +} + } // namespace chrome diff --git a/chrome/tools/build/apply_locales.py b/chrome/tools/build/apply_locales.py new file mode 100644 index 0000000..43d064a --- /dev/null +++ b/chrome/tools/build/apply_locales.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# TODO: remove this script when GYP has for loops + +import sys + +def main(argv): + if len(argv) < 3: + print 'ERROR: need string and list of locales' + return 1 + + str_template = argv[1] + locales = argv[2:] + + results = [] + for locale in locales: + results.append(str_template.replace('ZZLOCALE', locale)) + + # Quote each element so filename spaces don't mess up GYP's attempt to parse + # it into a list. + print " ".join(['"%s"' % x for x in results]) + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/chrome/tools/mac_helpers/infoplist_strings_util.mm b/chrome/tools/mac_helpers/infoplist_strings_util.mm new file mode 100644 index 0000000..70fbca6 --- /dev/null +++ b/chrome/tools/mac_helpers/infoplist_strings_util.mm @@ -0,0 +1,296 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Helper tool that is built and run during a build to pull strings from +// the GRD files and generate the InfoPlist.strings files needed for +// Mac OS X app bundles. + +#import <Foundation/Foundation.h> + +#include <stdio.h> +#include <unistd.h> + +#include "base/data_pack.h" +#include "base/file_path.h" +#include "base/scoped_nsautorelease_pool.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "grit/chromium_strings.h" + +namespace { + +NSString* ApplicationVersionString(const char* version_file_path) { + NSError* error = nil; + NSString* path_string = [NSString stringWithUTF8String:version_file_path]; + NSString* version_file = + [NSString stringWithContentsOfFile:path_string + encoding:NSUTF8StringEncoding + error:&error]; + if (!version_file || error) { + fprintf(stderr, "Failed to load version file: %s\n", + [[error description] UTF8String]); + return nil; + } + + int major = 0, minor = 0, build = 0, patch = 0; + NSScanner* scanner = [NSScanner scannerWithString:version_file]; + if ([scanner scanString:@"MAJOR=" intoString:nil] && + [scanner scanInt:&major] && + [scanner scanString:@"MINOR=" intoString:nil] && + [scanner scanInt:&minor] && + [scanner scanString:@"BUILD=" intoString:nil] && + [scanner scanInt:&build] && + [scanner scanString:@"PATCH=" intoString:nil] && + [scanner scanInt:&patch]) { + return [NSString stringWithFormat:@"%d.%d.%d.%d", + major, minor, build, patch]; + } + fprintf(stderr, "Failed to parse version file\n"); + return nil; +} + +base::DataPack* LoadResourceDataPack(const char* dir_path, + const char* branding_strings_name, + const char* locale_name) { + base::DataPack* resource_pack = NULL; + + NSString* resource_path = [NSString stringWithFormat:@"%s/%s_%s.pak", + dir_path, branding_strings_name, locale_name]; + if (resource_path) { + FilePath resources_pak_path([resource_path fileSystemRepresentation]); + resource_pack = new base::DataPack; + bool success = resource_pack->Load(resources_pak_path); + if (!success) { + delete resource_pack; + resource_pack = NULL; + } + } + + return resource_pack; +} + +NSString* LoadStringFromDataPack(base::DataPack* data_pack, + const char* data_pack_lang, + uint32_t resource_id, + const char* resource_id_str) { + NSString* result = nil; + StringPiece data; + if (data_pack->Get(resource_id, &data)) { + // Data pack encodes strings as UTF16. + result = + [[[NSString alloc] initWithBytes:data.data() + length:data.length() + encoding:NSUTF16LittleEndianStringEncoding] + autorelease]; + } + if (!result) { + fprintf(stderr, "ERROR: failed to load string %s for lang %s\n", + resource_id_str, data_pack_lang); + exit(1); + } + return result; +} + +// Escape quotes, newlines, etc so there are no errors when the strings file +// is parsed. +NSString* EscapeForStringsFileValue(NSString* str) { + NSMutableString* worker = [NSMutableString stringWithString:str]; + + // Since this is a build tool, we don't really worry about making this + // the most efficient code. + + // Backslash first since we need to do it before we put in all the others + [worker replaceOccurrencesOfString:@"\\" + withString:@"\\\\" + options:NSLiteralSearch + range:NSMakeRange(0, [worker length])]; + // Now the rest of them. + [worker replaceOccurrencesOfString:@"\n" + withString:@"\\n" + options:NSLiteralSearch + range:NSMakeRange(0, [worker length])]; + [worker replaceOccurrencesOfString:@"\r" + withString:@"\\r" + options:NSLiteralSearch + range:NSMakeRange(0, [worker length])]; + [worker replaceOccurrencesOfString:@"\t" + withString:@"\\t" + options:NSLiteralSearch + range:NSMakeRange(0, [worker length])]; + [worker replaceOccurrencesOfString:@"\"" + withString:@"\\\"" + options:NSLiteralSearch + range:NSMakeRange(0, [worker length])]; + + return [[worker copy] autorelease]; +} + +// The valid types for the -t arg +const char* kAppType_Main = "main"; // Main app +const char* kAppType_Helper = "helper"; // Helper app + +} // namespace + +int main(int argc, char* const argv[]) { + base::ScopedNSAutoreleasePool autorelease_pool; + + const char* version_file_path = NULL; + const char* grit_output_dir = NULL; + const char* branding_strings_name = NULL; + const char* output_dir = NULL; + const char* app_type = kAppType_Main; + + // Process the args + int ch; + while ((ch = getopt(argc, argv, "t:v:g:b:o:")) != -1) { + switch (ch) { + case 't': + app_type = optarg; + break; + case 'v': + version_file_path = optarg; + break; + case 'g': + grit_output_dir = optarg; + break; + case 'b': + branding_strings_name = optarg; + break; + case 'o': + output_dir = optarg; + break; + default: + fprintf(stderr, "ERROR: bad command line arg\n"); + exit(1); + break; + } + } + argc -= optind; + argv += optind; + +#define CHECK_ARG(a, b) \ + do { \ + if ((a)) { \ + fprintf(stderr, "ERROR: " b "\n"); \ + exit(1); \ + } \ + } while (false) + + // Check our args + CHECK_ARG(!version_file_path, "Missing VERSION file path"); + CHECK_ARG(!grit_output_dir, "Missing grit output dir path"); + CHECK_ARG(!output_dir, "Missing path to write InfoPlist.strings files"); + CHECK_ARG(!branding_strings_name, "Missing branding strings file name"); + CHECK_ARG(argc == 0, "Missing language list"); + CHECK_ARG((strcmp(app_type, kAppType_Main) != 0 && + strcmp(app_type, kAppType_Helper) != 0), + "Unknown app type"); + + char* const* lang_list = argv; + int lang_list_count = argc; + + // Parse the version file and build our string + NSString* version_string = ApplicationVersionString(version_file_path); + if (!version_string) { + fprintf(stderr, "ERROR: failed to get a version string"); + exit(1); + } + + NSFileManager* fm = [NSFileManager defaultManager]; + + for (int loop = 0; loop < lang_list_count; ++loop) { + const char* cur_lang = lang_list[loop]; + + // Open the branded string pak file + scoped_ptr<base::DataPack> branded_data_pack( + LoadResourceDataPack(grit_output_dir, + branding_strings_name, + cur_lang)); + if (branded_data_pack.get() == NULL) { + fprintf(stderr, "ERROR: Failed to load branded pak for language: %s\n", + cur_lang); + exit(1); + } + + uint32_t name_id = IDS_PRODUCT_NAME; + const char* name_id_str = "IDS_PRODUCT_NAME"; + uint32_t short_name_id = IDS_SHORT_PRODUCT_NAME; + const char* short_name_id_str = "IDS_SHORT_PRODUCT_NAME"; + if (strcmp(app_type, kAppType_Helper) == 0) { + name_id = IDS_HELPER_NAME; + name_id_str = "IDS_HELPER_NAME"; + short_name_id = IDS_SHORT_HELPER_NAME; + short_name_id_str = "IDS_SHORT_HELPER_NAME"; + } + + // Fetch the strings + NSString* name = + LoadStringFromDataPack(branded_data_pack.get(), cur_lang, + name_id, name_id_str); + NSString* short_name = + LoadStringFromDataPack(branded_data_pack.get(), cur_lang, + short_name_id, short_name_id_str); + NSString* copyright = + LoadStringFromDataPack(branded_data_pack.get(), cur_lang, + IDS_ABOUT_VERSION_COPYRIGHT, + "IDS_ABOUT_VERSION_COPYRIGHT"); + + // For now, assume this is ok for all languages. If we need to, this could + // be moved into generated_resources.grd and fetched. + NSString *get_info = [NSString stringWithFormat:@"%@ %@, %@", + name, version_string, copyright]; + + // Generate the InfoPlist.strings file contents + NSString* strings_file_contents_string = + [NSString stringWithFormat: + @"CFBundleDisplayName = \"%@\";\n" + @"CFBundleGetInfoString = \"%@\";\n" + @"CFBundleName = \"%@\";\n" + @"NSHumanReadableCopyright = \"%@\";\n", + EscapeForStringsFileValue(name), + EscapeForStringsFileValue(get_info), + EscapeForStringsFileValue(short_name), + EscapeForStringsFileValue(copyright)]; + + // We set up Xcode projects expecting strings files to be UTF8, so make + // sure we write the data in that form. When Xcode copies them it will + // put them final runtime encoding. + NSData* strings_file_contents_utf8 = + [strings_file_contents_string dataUsingEncoding:NSUTF8StringEncoding]; + + if ([strings_file_contents_utf8 length] == 0) { + fprintf(stderr, "ERROR: failed to get the utf8 encoding of the strings " + "file for language: %s\n", cur_lang); + exit(1); + } + + // Make sure the lproj we write to exists + NSString *output_path = + [[NSString stringWithUTF8String:output_dir] + stringByAppendingPathComponent: + [NSString stringWithFormat:@"%s.lproj", cur_lang]]; + NSError* error = nil; + if (![fm fileExistsAtPath:output_path] && + ![fm createDirectoryAtPath:output_path + withIntermediateDirectories:YES + attributes:nil + error:&error]) { + fprintf(stderr, "ERROR: '%s' didn't exist or we failed to create it\n", + [output_path UTF8String]); + exit(1); + } + + // Write out the file + output_path = + [output_path stringByAppendingPathComponent:@"InfoPlist.strings"]; + if (![strings_file_contents_utf8 writeToFile:output_path + atomically:YES]) { + fprintf(stderr, "ERROR: Failed to write out '%s'\n", + [output_path UTF8String]); + exit(1); + } + } + return 0; +} |