Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: chrome/tools/mac_helpers/infoplist_strings_util.mm

Issue 2783413002: Move //chrome/tools/mac_helpers:infoplist_strings_util to //chrome/tools/build/mac. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/tools/mac_helpers/DEPS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Helper tool that is built and run during a build to pull strings from
6 // the GRD files and generate the InfoPlist.strings files needed for
7 // Mac OS X app bundles.
8
9 #import <Foundation/Foundation.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <unistd.h>
13
14 #include <memory>
15
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/i18n/icu_util.h"
19 #include "base/i18n/message_formatter.h"
20 #include "base/mac/scoped_nsautorelease_pool.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/sys_string_conversions.h"
25 #include "base/time/time.h"
26 #include "chrome/grit/chromium_strings.h"
27 #include "ui/base/resource/data_pack.h"
28
29 namespace {
30
31 NSString* ApplicationVersionString(const char* version_file_path) {
32 NSError* error = nil;
33 NSString* path_string = [NSString stringWithUTF8String:version_file_path];
34 NSString* version_file =
35 [NSString stringWithContentsOfFile:path_string
36 encoding:NSUTF8StringEncoding
37 error:&error];
38 if (!version_file || error) {
39 fprintf(stderr, "Failed to load version file: %s\n",
40 [[error description] UTF8String]);
41 return nil;
42 }
43
44 int major = 0, minor = 0, build = 0, patch = 0;
45 NSScanner* scanner = [NSScanner scannerWithString:version_file];
46 if ([scanner scanString:@"MAJOR=" intoString:nil] &&
47 [scanner scanInt:&major] &&
48 [scanner scanString:@"MINOR=" intoString:nil] &&
49 [scanner scanInt:&minor] &&
50 [scanner scanString:@"BUILD=" intoString:nil] &&
51 [scanner scanInt:&build] &&
52 [scanner scanString:@"PATCH=" intoString:nil] &&
53 [scanner scanInt:&patch]) {
54 return [NSString stringWithFormat:@"%d.%d.%d.%d",
55 major, minor, build, patch];
56 }
57 fprintf(stderr, "Failed to parse version file\n");
58 return nil;
59 }
60
61 ui::DataPack* LoadResourceDataPack(const char* dir_path,
62 const char* branding_strings_name,
63 const char* locale_name) {
64 ui::DataPack* resource_pack = NULL;
65
66 NSString* resource_path = [NSString stringWithFormat:@"%s/%s_%s.pak",
67 dir_path, branding_strings_name, locale_name];
68 if (resource_path) {
69 base::FilePath resources_pak_path([resource_path fileSystemRepresentation]);
70 resources_pak_path = base::MakeAbsoluteFilePath(resources_pak_path);
71 resource_pack = new ui::DataPack(ui::SCALE_FACTOR_100P);
72 bool success = resource_pack->LoadFromPath(resources_pak_path);
73 if (!success) {
74 delete resource_pack;
75 resource_pack = NULL;
76 }
77 }
78
79 return resource_pack;
80 }
81
82 NSString* LoadStringFromDataPack(ui::DataPack* data_pack,
83 const char* data_pack_lang,
84 uint32_t resource_id,
85 const char* resource_id_str) {
86 NSString* result = nil;
87 base::StringPiece data;
88 if (data_pack->GetStringPiece(resource_id, &data)) {
89 // Data pack encodes strings as either UTF8 or UTF16.
90 if (data_pack->GetTextEncodingType() == ui::DataPack::UTF8) {
91 result =
92 [[[NSString alloc] initWithBytes:data.data()
93 length:data.length()
94 encoding:NSUTF8StringEncoding]
95 autorelease];
96 } else if (data_pack->GetTextEncodingType() == ui::DataPack::UTF16) {
97 result =
98 [[[NSString alloc] initWithBytes:data.data()
99 length:data.length()
100 encoding:NSUTF16LittleEndianStringEncoding]
101 autorelease];
102 } else {
103 fprintf(stderr, "ERROR: requested string %s from binary data pack\n",
104 resource_id_str);
105 exit(1);
106 }
107 }
108 if (!result) {
109 fprintf(stderr, "ERROR: failed to load string %s for lang %s\n",
110 resource_id_str, data_pack_lang);
111 exit(1);
112 }
113 return result;
114 }
115
116 // Escape quotes, newlines, etc so there are no errors when the strings file
117 // is parsed.
118 NSString* EscapeForStringsFileValue(NSString* str) {
119 NSMutableString* worker = [NSMutableString stringWithString:str];
120
121 // Since this is a build tool, we don't really worry about making this
122 // the most efficient code.
123
124 // Backslash first since we need to do it before we put in all the others
125 [worker replaceOccurrencesOfString:@"\\"
126 withString:@"\\\\"
127 options:NSLiteralSearch
128 range:NSMakeRange(0, [worker length])];
129 // Now the rest of them.
130 [worker replaceOccurrencesOfString:@"\n"
131 withString:@"\\n"
132 options:NSLiteralSearch
133 range:NSMakeRange(0, [worker length])];
134 [worker replaceOccurrencesOfString:@"\r"
135 withString:@"\\r"
136 options:NSLiteralSearch
137 range:NSMakeRange(0, [worker length])];
138 [worker replaceOccurrencesOfString:@"\t"
139 withString:@"\\t"
140 options:NSLiteralSearch
141 range:NSMakeRange(0, [worker length])];
142 [worker replaceOccurrencesOfString:@"\""
143 withString:@"\\\""
144 options:NSLiteralSearch
145 range:NSMakeRange(0, [worker length])];
146
147 return [[worker copy] autorelease];
148 }
149
150 // The valid types for the -t arg
151 const char kAppType_Main[] = "main"; // Main app
152 const char kAppType_Helper[] = "helper"; // Helper app
153
154 } // namespace
155
156 int main(int argc, char* const argv[]) {
157 base::mac::ScopedNSAutoreleasePool autorelease_pool;
158
159 const char* version_file_path = NULL;
160 const char* grit_output_dir = NULL;
161 const char* branding_strings_name = NULL;
162 const char* output_dir = NULL;
163 const char* app_type = kAppType_Main;
164
165 // Process the args
166 int ch;
167 while ((ch = getopt(argc, argv, "t:v:g:b:o:")) != -1) {
168 switch (ch) {
169 case 't':
170 app_type = optarg;
171 break;
172 case 'v':
173 version_file_path = optarg;
174 break;
175 case 'g':
176 grit_output_dir = optarg;
177 break;
178 case 'b':
179 branding_strings_name = optarg;
180 break;
181 case 'o':
182 output_dir = optarg;
183 break;
184 default:
185 fprintf(stderr, "ERROR: bad command line arg\n");
186 exit(1);
187 break;
188 }
189 }
190 argc -= optind;
191 argv += optind;
192
193 #define CHECK_ARG(a, b) \
194 do { \
195 if ((a)) { \
196 fprintf(stderr, "ERROR: " b "\n"); \
197 exit(1); \
198 } \
199 } while (false)
200
201 // Check our args
202 CHECK_ARG(!version_file_path, "Missing VERSION file path");
203 CHECK_ARG(!grit_output_dir, "Missing grit output dir path");
204 CHECK_ARG(!output_dir, "Missing path to write InfoPlist.strings files");
205 CHECK_ARG(!branding_strings_name, "Missing branding strings file name");
206 CHECK_ARG(argc == 0, "Missing language list");
207 CHECK_ARG((strcmp(app_type, kAppType_Main) != 0 &&
208 strcmp(app_type, kAppType_Helper) != 0),
209 "Unknown app type");
210
211 char* const* lang_list = argv;
212 int lang_list_count = argc;
213
214 base::i18n::InitializeICU();
215
216 // Parse the version file and build our string
217 NSString* version_string = ApplicationVersionString(version_file_path);
218 if (!version_string) {
219 fprintf(stderr, "ERROR: failed to get a version string");
220 exit(1);
221 }
222
223 NSFileManager* fm = [NSFileManager defaultManager];
224
225 for (int loop = 0; loop < lang_list_count; ++loop) {
226 const char* cur_lang = lang_list[loop];
227
228 // Open the branded string pak file
229 std::unique_ptr<ui::DataPack> branded_data_pack(
230 LoadResourceDataPack(grit_output_dir, branding_strings_name, cur_lang));
231 if (branded_data_pack.get() == NULL) {
232 fprintf(stderr, "ERROR: Failed to load branded pak for language: %s\n",
233 cur_lang);
234 exit(1);
235 }
236
237 uint32_t name_id = IDS_PRODUCT_NAME;
238 const char* name_id_str = "IDS_PRODUCT_NAME";
239 uint32_t short_name_id = IDS_APP_MENU_PRODUCT_NAME;
240 const char* short_name_id_str = "IDS_APP_MENU_PRODUCT_NAME";
241 if (strcmp(app_type, kAppType_Helper) == 0) {
242 name_id = IDS_HELPER_NAME;
243 name_id_str = "IDS_HELPER_NAME";
244 short_name_id = IDS_SHORT_HELPER_NAME;
245 short_name_id_str = "IDS_SHORT_HELPER_NAME";
246 }
247
248 // Fetch the strings
249 NSString* name =
250 LoadStringFromDataPack(branded_data_pack.get(), cur_lang,
251 name_id, name_id_str);
252 NSString* short_name =
253 LoadStringFromDataPack(branded_data_pack.get(), cur_lang,
254 short_name_id, short_name_id_str);
255 NSString* copyright_format =
256 LoadStringFromDataPack(branded_data_pack.get(), cur_lang,
257 IDS_ABOUT_VERSION_COPYRIGHT,
258 "IDS_ABOUT_VERSION_COPYRIGHT");
259 NSString* address_book_prompt_description =
260 LoadStringFromDataPack(branded_data_pack.get(), cur_lang,
261 IDS_AUTOFILL_ADDRESS_BOOK_PROMPT_DESCRIPTION,
262 "IDS_AUTOFILL_ADDRESS_BOOK_PROMPT_DESCRIPTION");
263
264 NSString* copyright = base::SysUTF16ToNSString(
265 base::i18n::MessageFormatter::FormatWithNumberedArgs(
266 base::SysNSStringToUTF16(copyright_format), base::Time::Now()));
267
268 // For now, assume this is ok for all languages. If we need to, this could
269 // be moved into generated_resources.grd and fetched.
270 NSString *get_info = [NSString stringWithFormat:@"%@ %@, %@",
271 name, version_string, copyright];
272
273 // Generate the InfoPlist.strings file contents
274 NSString* strings_file_contents_string =
275 [NSString stringWithFormat:
276 @"CFBundleDisplayName = \"%@\";\n"
277 @"CFBundleGetInfoString = \"%@\";\n"
278 @"CFBundleName = \"%@\";\n"
279 @"NSContactsUsageDescription = \"%@\";\n"
280 @"NSHumanReadableCopyright = \"%@\";\n",
281 EscapeForStringsFileValue(name),
282 EscapeForStringsFileValue(get_info),
283 EscapeForStringsFileValue(short_name),
284 EscapeForStringsFileValue(address_book_prompt_description),
285 EscapeForStringsFileValue(copyright)];
286
287 // We set up Xcode projects expecting strings files to be UTF8, so make
288 // sure we write the data in that form. When Xcode copies them it will
289 // put them final runtime encoding.
290 NSData* strings_file_contents_utf8 =
291 [strings_file_contents_string dataUsingEncoding:NSUTF8StringEncoding];
292
293 if ([strings_file_contents_utf8 length] == 0) {
294 fprintf(stderr, "ERROR: failed to get the utf8 encoding of the strings "
295 "file for language: %s\n", cur_lang);
296 exit(1);
297 }
298
299 // For Cocoa to find the locale at runtime, it needs to use '_' instead of
300 // '-' (http://crbug.com/20441). Also, 'en-US' should be represented
301 // simply as 'en' (http://crbug.com/19165, http://crbug.com/25578).
302 NSString* cur_lang_ns = [NSString stringWithUTF8String:cur_lang];
303 if ([cur_lang_ns isEqualToString:@"en-US"]) {
304 cur_lang_ns = @"en";
305 }
306 cur_lang_ns = [cur_lang_ns stringByReplacingOccurrencesOfString:@"-"
307 withString:@"_"];
308 // Make sure the lproj we write to exists
309 NSString *lproj_name = [NSString stringWithFormat:@"%@.lproj", cur_lang_ns];
310 NSString *output_path =
311 [[NSString stringWithUTF8String:output_dir]
312 stringByAppendingPathComponent:lproj_name];
313 NSError* error = nil;
314 if (![fm fileExistsAtPath:output_path] &&
315 ![fm createDirectoryAtPath:output_path
316 withIntermediateDirectories:YES
317 attributes:nil
318 error:&error]) {
319 fprintf(stderr, "ERROR: '%s' didn't exist or we failed to create it\n",
320 [output_path UTF8String]);
321 exit(1);
322 }
323
324 // Write out the file
325 output_path =
326 [output_path stringByAppendingPathComponent:@"InfoPlist.strings"];
327 if (![strings_file_contents_utf8 writeToFile:output_path
328 atomically:YES]) {
329 fprintf(stderr, "ERROR: Failed to write out '%s'\n",
330 [output_path UTF8String]);
331 exit(1);
332 }
333 }
334 return 0;
335 }
OLDNEW
« no previous file with comments | « chrome/tools/mac_helpers/DEPS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698