diff options
author | sdefresne <sdefresne@chromium.org> | 2015-10-05 02:23:39 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-05 09:24:34 +0000 |
commit | a213cebe6af1a80367336908d37f30ab6542940d (patch) | |
tree | f48f639295d3d4d5afa4f2346be26f6e412765ad | |
parent | dba5fc94c1c009edef581ed788f954c8c980ee52 (diff) | |
download | chromium_src-a213cebe6af1a80367336908d37f30ab6542940d.zip chromium_src-a213cebe6af1a80367336908d37f30ab6542940d.tar.gz chromium_src-a213cebe6af1a80367336908d37f30ab6542940d.tar.bz2 |
Componentize script to generate UI string overrides mapping.
Change the API of the script to generate both the header and source file
and to take a list of header files as input (to allow overridding strings
from components/components_strings.grd).
The script is now generating a method CreateUIStringOverrider() and hides
the arrays in the implementation file and allow customization of the
namespace in which the function and code is generated.
Introduce a new target "chrome_ui_string_overrider_factory" in the gyp
build to mimic the same target in the gn build (compiles the source file
generated by the script).
Rename the script from to generate_ui_string_overrider.py to reflect the
new role of the script and move it to components/variations/service.
Introduce .gni file to help using the script by different embedders (don't
introduce .gypi files as it is much harder to share code for gyp).
Componentize ui_string_overrider_unittest.cc.
BUG=534257
Review URL: https://codereview.chromium.org/1374773002
Cr-Commit-Position: refs/heads/master@{#352308}
16 files changed, 483 insertions, 290 deletions
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn index 0965e6d..a5c37e7 100644 --- a/chrome/app/BUILD.gn +++ b/chrome/app/BUILD.gn @@ -134,38 +134,6 @@ if (is_android) { } } -# GYP version: chrome/chrome_resources.gyp:chrome_strings_map -action("make_generated_resources_map") { - # Targets should depend on generated_resources_map instead. - visibility = [ ":generated_resources_map" ] - - script = "//chrome/browser/metrics/variations/generate_resources_map.py" - - inputs = [ - "$root_gen_dir/chrome/grit/generated_resources.h", - ] - outputs = [ - "$root_gen_dir/chrome/generated_resources_map.cc", - ] - - args = - rebase_path(inputs, root_build_dir) + rebase_path(outputs, root_build_dir) - - deps = [ - ":generated_resources", - ] -} - -# Collect the generated .cc file from make_generated_resources_map and put it -# in a source set so targets that depend on it will link the source rather than -# specifying it manually. This doesn't happen in the GYP build. -source_set("generated_resources_map") { - sources = get_target_outputs(":make_generated_resources_map") - deps = [ - ":make_generated_resources_map", - ] -} - # GYP version: chrome/chrome_resources.gyp:chrome_strings # (generate_google_chrome_strings action) grit("google_chrome_strings") { diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 9fa3cf0..dab46f1 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -93,9 +93,9 @@ source_set("browser") { "//chrome:extra_resources", "//chrome:resources", "//chrome:strings", - "//chrome/app:generated_resources_map", "//chrome/app/resources:platform_locale_settings", "//chrome/app/theme:theme_resources", + "//chrome/browser/metrics/variations:chrome_ui_string_overrider_factory", "//chrome/browser/net:probe_message_proto", "//chrome/browser/ui", "//chrome/common", @@ -264,9 +264,9 @@ source_set("browser") { deps += [ "//apps", "//cc", - "//chrome/app:generated_resources_map", "//chrome/app/theme:theme_resources", "//chrome/browser/devtools", + "//chrome/browser/metrics/variations:chrome_ui_string_overrider_factory", "//chrome/browser/resources:component_extension_resources", "//chrome/common/net", "//chrome/installer/util", diff --git a/chrome/browser/metrics/metrics_services_manager.cc b/chrome/browser/metrics/metrics_services_manager.cc index a981620..6e71aa1 100644 --- a/chrome/browser/metrics/metrics_services_manager.cc +++ b/chrome/browser/metrics/metrics_services_manager.cc @@ -14,7 +14,7 @@ #include "chrome/browser/metrics/chrome_metrics_service_client.h" #include "chrome/browser/metrics/metrics_reporting_state.h" #include "chrome/browser/metrics/variations/chrome_variations_service_client.h" -#include "chrome/browser/metrics/variations/generated_resources_map.h" +#include "chrome/browser/metrics/variations/ui_string_overrider_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_otr_state.h" @@ -69,9 +69,7 @@ variations::VariationsService* MetricsServicesManager::GetVariationsService() { variations_service_ = variations::VariationsService::Create( make_scoped_ptr(new ChromeVariationsServiceClient()), local_state_, GetMetricsStateManager(), switches::kDisableBackgroundNetworking, - variations::UIStringOverrider(chrome_variations::kResourceHashes, - chrome_variations::kResourceIndices, - chrome_variations::kNumResources)); + chrome_variations::CreateUIStringOverrider()); } return variations_service_.get(); } diff --git a/chrome/browser/metrics/variations/BUILD.gn b/chrome/browser/metrics/variations/BUILD.gn new file mode 100644 index 0000000..ed39b8c --- /dev/null +++ b/chrome/browser/metrics/variations/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2015 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. + +import("//components/variations/service/generate_ui_string_overrider.gni") + +# GYP version: chrome/chrome_resources.gyp:chrome_ui_string_overrider_factory +generate_ui_string_overrider("chrome_ui_string_overrider_factory") { + inputs = [ + "$root_gen_dir/chrome/grit/generated_resources.h", + ] + deps = [ + "//chrome/app:generated_resources", + ] + namespace = "chrome_variations" + header_filename = "ui_string_overrider_factory.h" + source_filename = "ui_string_overrider_factory.cc" +} diff --git a/chrome/browser/metrics/variations/generate_resources_map.py b/chrome/browser/metrics/variations/generate_resources_map.py deleted file mode 100755 index 6cf535b..0000000 --- a/chrome/browser/metrics/variations/generate_resources_map.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/python -# Copyright 2014 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. - -import collections -import hashlib -import operator -import os -import re -import sys - - -RESOURCE_EXTRACT_REGEX = re.compile('^#define (\S*) (\d*)$', re.MULTILINE) - -class Error(Exception): - """Base error class for all exceptions in generated_resources_map.""" - - -class HashCollisionError(Error): - """Multiple resource names hash to the same value.""" - - -Resource = collections.namedtuple("Resource", ['hash', 'name', 'index']) - - -def _HashName(name): - """Returns the hash id for a name. - - Args: - name: The name to hash. - - Returns: - An int that is at most 32 bits. - """ - md5hash = hashlib.md5() - md5hash.update(name) - return int(md5hash.hexdigest()[:8], 16) - - -def _GetNameIndexPairsIter(string_to_scan): - """Gets an iterator of the resource name and index pairs of the given string. - - Scans the input string for lines of the form "#define NAME INDEX" and returns - an iterator over all matching (NAME, INDEX) pairs. - - Args: - string_to_scan: The input string to scan. - - Yields: - A tuple of name and index. - """ - for match in RESOURCE_EXTRACT_REGEX.finditer(string_to_scan): - yield match.group(1, 2) - - -def _GetResourceListFromString(resources_content): - """Produces a list of |Resource| objects from a string. - - The input string conaints lines of the form "#define NAME INDEX". The returned - list is sorted primarily by hash, then name, and then index. - - Args: - resources_content: The input string to process, contains lines of the form - "#define NAME INDEX". - - Returns: - A sorted list of |Resource| objects. - """ - resources = [Resource(_HashName(name), name, index) for name, index in - _GetNameIndexPairsIter(resources_content)] - - # The default |Resource| order makes |resources| sorted by the hash, then - # name, then index. - resources.sort() - - return resources - - -def _CheckForHashCollisions(sorted_resource_list): - """Checks a sorted list of |Resource| objects for hash collisions. - - Args: - sorted_resource_list: A sorted list of |Resource| objects. - - Returns: - A set of all |Resource| objects with collisions. - """ - collisions = set() - for i in xrange(len(sorted_resource_list) - 1): - resource = sorted_resource_list[i] - next_resource = sorted_resource_list[i+1] - if resource.hash == next_resource.hash: - collisions.add(resource) - collisions.add(next_resource) - - return collisions - - -def _GenDataArray( - resources, entry_pattern, array_name, array_type, data_getter): - """Generates a C++ statement defining a literal array containing the hashes. - - Args: - resources: A sorted list of |Resource| objects. - entry_pattern: A pattern to be used to generate each entry in the array. The - pattern is expected to have a place for data and one for a comment, in - that order. - array_name: The name of the array being generated. - array_type: The type of the array being generated. - data_getter: A function that gets the array data from a |Resource| object. - - Returns: - A string containing a C++ statement defining the an array. - """ - lines = [entry_pattern % (data_getter(r), r.name) for r in resources] - pattern = """const %(type)s %(name)s[] = { -%(content)s -}; -""" - return pattern % {'type': array_type, - 'name': array_name, - 'content': '\n'.join(lines)} - - -def _GenerateFileContent(resources_content): - """Generates the .cc content from the given generated_resources.h content. - - Args: - resources_content: The input string to process, contains lines of the form - "#define NAME INDEX". - - Returns: - .cc file content defining the kResourceHashes and kResourceIndices arrays. - """ - hashed_tuples = _GetResourceListFromString(resources_content) - - collisions = _CheckForHashCollisions(hashed_tuples) - if collisions: - error_message = "\n".join( - ["hash: %i, name: %s" % (i[0], i[1]) for i in sorted(collisions)]) - error_message = ("\nThe following names had hash collisions " - "(sorted by the hash value):\n%s\n" %(error_message)) - raise HashCollisionError(error_message) - - hashes_array = _GenDataArray( - hashed_tuples, " %iU, // %s", 'kResourceHashes', 'uint32_t', - operator.attrgetter('hash')) - indices_array = _GenDataArray( - hashed_tuples, " %s, // %s", 'kResourceIndices', 'int', - operator.attrgetter('index')) - - return ( - "// This file was generated by generate_resources_map.py. Do not edit.\n" - "\n\n" - "#include " - "\"chrome/browser/metrics/variations/generated_resources_map.h\"\n\n" - "namespace chrome_variations {\n\n" - "const size_t kNumResources = %i;\n\n" - "%s" - "\n" - "%s" - "\n" - "} // namespace chrome_variations\n") % ( - len(hashed_tuples), hashes_array, indices_array) - - -def main(resources_file, map_file): - generated_resources_h = "" - with open(resources_file, "r") as resources: - generated_resources_h = resources.read() - - if len(generated_resources_h) == 0: - raise Error("No content loaded for %s." % (resources_file)) - - file_content = _GenerateFileContent(generated_resources_h) - - with open(map_file, "w") as generated_file: - generated_file.write(file_content) - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1], sys.argv[2])) diff --git a/chrome/browser/metrics/variations/generated_resources_map.h b/chrome/browser/metrics/variations/generated_resources_map.h deleted file mode 100644 index c4c6cd8..0000000 --- a/chrome/browser/metrics/variations/generated_resources_map.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 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. - -#ifndef CHROME_BROWSER_METRICS_VARIATIONS_GENERATED_RESOURCES_MAP_H_ -#define CHROME_BROWSER_METRICS_VARIATIONS_GENERATED_RESOURCES_MAP_H_ - -#include "base/basictypes.h" - -namespace chrome_variations { - -// This file provides a mapping from hashes of generated resource names to their -// IDs. This mapping is achieved by having two arrays: |kResourceHashes|, a -// sorted array of resource name hashes; and |kResourceIndices|, an array of -// resource indices in the same order as |kResourceHashes|. So, if -// generated_resources.h contains |#define IDS_FOO 12345|, then for some index i -// kResourceHashes[i] = HASH("IDS_FOO") and kResourceIndices[i] = 12345. Both -// arrays are of length |kNumResources|. - -// The definitions of the arrays are generated by generate_resources_map.py from -// the content of generated_resources.h. - -// Length of |kResourceHashes| and |kResourceIndices|. -extern const size_t kNumResources; - -// A sorted array of hashed generated resource names. -extern const uint32_t kResourceHashes[]; - -// An array of generated resource indices. The order of this array corresponds -// to the order of |kResourceHashes|. -extern const int kResourceIndices[]; - -} // namespace chrome_variations - -#endif // CHROME_BROWSER_METRICS_VARIATIONS_GENERATED_RESOURCES_MAP_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 05d1720..1b9bd16 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1882,7 +1882,6 @@ 'browser/metrics/time_ticks_experiment_win.h', 'browser/metrics/variations/chrome_variations_service_client.cc', 'browser/metrics/variations/chrome_variations_service_client.h', - 'browser/metrics/variations/generated_resources_map.h', 'browser/metrics/variations/variations_registry_syncer_win.cc', 'browser/metrics/variations/variations_registry_syncer_win.h', ], @@ -3079,7 +3078,8 @@ 'chrome_resources.gyp:chrome_extra_resources', 'chrome_resources.gyp:chrome_resources', 'chrome_resources.gyp:chrome_strings', - 'chrome_resources.gyp:chrome_strings_map', + 'chrome_resources.gyp:chrome_ui_string_overrider_factory', + 'chrome_resources.gyp:make_chrome_ui_string_overrider_factory', 'chrome_resources.gyp:platform_locale_settings', 'chrome_resources.gyp:theme_resources', 'common', @@ -3307,10 +3307,6 @@ '<(grit_out_dir)/grit/component_extension_resources_map.cc', '<(grit_out_dir)/grit/settings_resources_map.cc', '<(grit_out_dir)/grit/theme_resources_map.cc', - - # This file is generated by - # chrome/browser/metrics/variations/generate_resources_map.py - '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/generated_resources_map.cc', ], 'conditions': [ ['toolkit_views==1', { diff --git a/chrome/chrome_resources.gyp b/chrome/chrome_resources.gyp index bf21b16..041e51d 100644 --- a/chrome/chrome_resources.gyp +++ b/chrome/chrome_resources.gyp @@ -281,31 +281,49 @@ ], }, { - # GN version: //chrome/app:make_generated_resources_map - 'target_name': 'chrome_strings_map', + # GN version: //chrome/browser/metrics/variations:chrome_ui_string_overrider_factory_gen_sources + 'target_name': 'make_chrome_ui_string_overrider_factory', 'type': 'none', + 'hard_dependency': 1, 'dependencies': [ 'chrome_strings', ], 'actions': [ { - 'action_name': 'generate_resources_map', + 'action_name': 'generate_ui_string_overrider', 'inputs': [ - 'browser/metrics/variations/generate_resources_map.py', + '../components/variations/service/generate_ui_string_overrider.py', '<(grit_out_dir)/grit/generated_resources.h' ], 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/generated_resources_map.cc', + '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/ui_string_overrider_factory.cc', + '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/ui_string_overrider_factory.h', ], 'action': [ 'python', - 'browser/metrics/variations/generate_resources_map.py', + '../components/variations/service/generate_ui_string_overrider.py', + '-N', 'chrome_variations', + '-o', '<(SHARED_INTERMEDIATE_DIR)', + '-S', 'chrome/browser/metrics/variations/ui_string_overrider_factory.cc', + '-H', 'chrome/browser/metrics/variations/ui_string_overrider_factory.h', '<(grit_out_dir)/grit/generated_resources.h', - '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/generated_resources_map.cc' ], 'message': 'Generating generated resources map.', } ], }, { + # GN version: //chrome/browser/metrics/variations:chrome_ui_string_overrider_factory + 'target_name': 'chrome_ui_string_overrider_factory', + 'type': 'static_library', + 'dependencies': [ + '../components/components.gyp:variations_service', + 'make_chrome_ui_string_overrider_factory', + ], + 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/ui_string_overrider_factory.cc', + '<(SHARED_INTERMEDIATE_DIR)/chrome/browser/metrics/variations/ui_string_overrider_factory.h', + ], + }, + { # GN version: //chrome/app/resources:platform_locale_settings 'target_name': 'platform_locale_settings', 'type': 'none', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 5c08ca1..688edc5 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -139,7 +139,6 @@ 'browser/metrics/thread_watcher_android_unittest.cc', 'browser/metrics/thread_watcher_unittest.cc', 'browser/metrics/time_ticks_experiment_unittest.cc', - 'browser/metrics/variations/ui_string_overrider_unittest.cc', 'browser/mod_pagespeed/mod_pagespeed_metrics_unittest.cc', 'browser/net/chrome_network_delegate_unittest.cc', 'browser/net/dns_probe_runner_unittest.cc', diff --git a/components/components_tests.gyp b/components/components_tests.gyp index d6744af..2ad674b 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -718,6 +718,7 @@ 'variations/experiment_labels_unittest.cc', 'variations/metrics_util_unittest.cc', 'variations/net/variations_http_header_provider_unittest.cc', + 'variations/service/ui_string_overrider_unittest.cc', 'variations/service/variations_service_unittest.cc', 'variations/study_filtering_unittest.cc', 'variations/variations_associated_data_unittest.cc', diff --git a/components/variations/service/BUILD.gn b/components/variations/service/BUILD.gn index 385c724..c6cf5b1 100644 --- a/components/variations/service/BUILD.gn +++ b/components/variations/service/BUILD.gn @@ -30,6 +30,7 @@ source_set("service") { source_set("unit_tests") { testonly = true sources = [ + "ui_string_overrider_unittest.cc", "variations_service_unittest.cc", ] diff --git a/components/variations/service/generate_ui_string_overrider.gni b/components/variations/service/generate_ui_string_overrider.gni new file mode 100644 index 0000000..11de0a0 --- /dev/null +++ b/components/variations/service/generate_ui_string_overrider.gni @@ -0,0 +1,66 @@ +# Copyright 2015 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. + +# Runs the resources map generation script other the given header files to +# produce an output file and a source_set to build it. +# +# Parameters: +# inputs: +# List of file name to read. Each file should be an header file generated +# by grit with line like "#define IDS_FOO 12345". +# +# namespace (optional): +# Namespace in which the generated code should be scoped. If left empty, +# the code will be in the global namespace. +# +# header_filename: +# Name of the generated header file. +# +# source_filename: +# Name of the generated source file. +# +# deps (optional): +# List of targets to depend on. +# +template("generate_ui_string_overrider") { + # Copy "target_name" to allow restrict the visibility of the generation + # target to that target (as ":$target_name" will have a different meaning + # in the "action" block). + source_set_target_name = target_name + gen_action_target_name = target_name + "_gen_sources" + + action(gen_action_target_name) { + header_filename = "$target_gen_dir/" + invoker.header_filename + source_filename = "$target_gen_dir/" + invoker.source_filename + + visibility = [ ":$source_set_target_name" ] + script = "//components/variations/service/generate_ui_string_overrider.py" + outputs = [ + header_filename, + source_filename, + ] + + inputs = invoker.inputs + if (defined(invoker.deps)) { + deps = invoker.deps + } + + args = [ + "-N" + invoker.namespace, + "-o" + rebase_path(root_gen_dir, root_build_dir), + "-H" + rebase_path(header_filename, root_gen_dir), + "-S" + rebase_path(source_filename, root_gen_dir), + ] + rebase_path(inputs, root_build_dir) + } + + source_set(target_name) { + sources = get_target_outputs(":$gen_action_target_name") + deps = [ + "//components/variations/service", + ":$gen_action_target_name", + ] + + forward_variables_from(invoker, [ "visibility" ]) + } +} diff --git a/components/variations/service/generate_ui_string_overrider.py b/components/variations/service/generate_ui_string_overrider.py new file mode 100755 index 0000000..288272c --- /dev/null +++ b/components/variations/service/generate_ui_string_overrider.py @@ -0,0 +1,292 @@ +#!/usr/bin/python +# Copyright 2014 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. + +import argparse +import collections +import hashlib +import operator +import os +import re +import sys + +SCRIPT_NAME = "generate_ui_string_overrider.py" +RESOURCE_EXTRACT_REGEX = re.compile('^#define (\S*) (\d*)$', re.MULTILINE) + +class Error(Exception): + """Base error class for all exceptions in generated_resources_map.""" + + +class HashCollisionError(Error): + """Multiple resource names hash to the same value.""" + + +Resource = collections.namedtuple("Resource", ['hash', 'name', 'index']) + + +def _HashName(name): + """Returns the hash id for a name. + + Args: + name: The name to hash. + + Returns: + An int that is at most 32 bits. + """ + md5hash = hashlib.md5() + md5hash.update(name) + return int(md5hash.hexdigest()[:8], 16) + + +def _GetNameIndexPairsIter(string_to_scan): + """Gets an iterator of the resource name and index pairs of the given string. + + Scans the input string for lines of the form "#define NAME INDEX" and returns + an iterator over all matching (NAME, INDEX) pairs. + + Args: + string_to_scan: The input string to scan. + + Yields: + A tuple of name and index. + """ + for match in RESOURCE_EXTRACT_REGEX.finditer(string_to_scan): + yield match.group(1, 2) + + +def _GetResourceListFromString(resources_content): + """Produces a list of |Resource| objects from a string. + + The input string contains lines of the form "#define NAME INDEX". The returned + list is sorted primarily by hash, then name, and then index. + + Args: + resources_content: The input string to process, contains lines of the form + "#define NAME INDEX". + + Returns: + A sorted list of |Resource| objects. + """ + resources = [Resource(_HashName(name), name, index) for name, index in + _GetNameIndexPairsIter(resources_content)] + + # The default |Resource| order makes |resources| sorted by the hash, then + # name, then index. + resources.sort() + + return resources + + +def _CheckForHashCollisions(sorted_resource_list): + """Checks a sorted list of |Resource| objects for hash collisions. + + Args: + sorted_resource_list: A sorted list of |Resource| objects. + + Returns: + A set of all |Resource| objects with collisions. + """ + collisions = set() + for i in xrange(len(sorted_resource_list) - 1): + resource = sorted_resource_list[i] + next_resource = sorted_resource_list[i+1] + if resource.hash == next_resource.hash: + collisions.add(resource) + collisions.add(next_resource) + + return collisions + + +def _GenDataArray( + resources, entry_pattern, array_name, array_type, data_getter): + """Generates a C++ statement defining a literal array containing the hashes. + + Args: + resources: A sorted list of |Resource| objects. + entry_pattern: A pattern to be used to generate each entry in the array. The + pattern is expected to have a place for data and one for a comment, in + that order. + array_name: The name of the array being generated. + array_type: The type of the array being generated. + data_getter: A function that gets the array data from a |Resource| object. + + Returns: + A string containing a C++ statement defining the an array. + """ + lines = [entry_pattern % (data_getter(r), r.name) for r in resources] + pattern = """const %(type)s %(name)s[] = { +%(content)s +}; +""" + return pattern % {'type': array_type, + 'name': array_name, + 'content': '\n'.join(lines)} + + +def _GenerateNamespacePrefixAndSuffix(namespace): + """Generates the namespace prefix and suffix for |namespace|. + + Args: + namespace: A string corresponding to the namespace name. May be empty. + + Returns: + A tuple of strings corresponding to the namespace prefix and suffix for + putting the code in the corresponding namespace in C++. If namespace is + the empty string, both returned strings are empty too. + """ + if not namespace: + return "", "" + return "namespace %s {\n\n" % namespace, "\n} // namespace %s\n" % namespace + + +def _GenerateSourceFileContent(resources_content, namespace, header_filename): + """Generates the .cc content from the given generated grit headers content. + + Args: + resources_content: The input string to process, contains lines of the form + "#define NAME INDEX". + + namespace: The namespace in which the generated code should be scoped. If + not defined, then the code will be in the global namespace. + + header_filename: Path to the corresponding .h. + + Returns: + .cc file content implementing the CreateUIStringOverrider() factory. + """ + hashed_tuples = _GetResourceListFromString(resources_content) + + collisions = _CheckForHashCollisions(hashed_tuples) + if collisions: + error_message = "\n".join( + ["hash: %i, name: %s" % (i[0], i[1]) for i in sorted(collisions)]) + error_message = ("\nThe following names had hash collisions " + "(sorted by the hash value):\n%s\n" %(error_message)) + raise HashCollisionError(error_message) + + hashes_array = _GenDataArray( + hashed_tuples, " %iU, // %s", 'kResourceHashes', 'uint32_t', + operator.attrgetter('hash')) + indices_array = _GenDataArray( + hashed_tuples, " %s, // %s", 'kResourceIndices', 'int', + operator.attrgetter('index')) + + namespace_prefix, namespace_suffix = _GenerateNamespacePrefixAndSuffix( + namespace) + + return ( + "// This file was generated by %(script_name)s. Do not edit.\n" + "\n" + "#include \"%(header_filename)s\"\n\n" + "%(namespace_prefix)s" + "namespace {\n\n" + "const size_t kNumResources = %(num_resources)i;\n\n" + "%(hashes_array)s" + "\n" + "%(indices_array)s" + "\n" + "} // namespace\n" + "\n" + "variations::UIStringOverrider CreateUIStringOverrider() {\n" + " return variations::UIStringOverrider(\n" + " kResourceHashes, kResourceIndices, kNumResources);\n" + "}\n" + "%(namespace_suffix)s") % { + 'script_name': SCRIPT_NAME, + 'header_filename': header_filename, + 'namespace_prefix': namespace_prefix, + 'num_resources': len(hashed_tuples), + 'hashes_array': hashes_array, + 'indices_array': indices_array, + 'namespace_suffix': namespace_suffix, + } + + +def _GenerateHeaderFileContent(namespace, header_filename): + """Generates the .h for to the .cc generated by _GenerateSourceFileContent. + + Args: + namespace: The namespace in which the generated code should be scoped. If + not defined, then the code will be in the global namespace. + + header_filename: Path to the corresponding .h. Used to generate the include + guards. + + Returns: + .cc file content implementing the CreateUIStringOverrider() factory. + """ + + include_guard = re.sub('[^A-Z]', '_', header_filename.upper()) + '_' + namespace_prefix, namespace_suffix = _GenerateNamespacePrefixAndSuffix( + namespace) + + return ( + "// This file was generated by %(script_name)s. Do not edit.\n" + "\n" + "#ifndef %(include_guard)s\n" + "#define %(include_guard)s\n" + "\n" + "#include \"components/variations/service/ui_string_overrider.h\"\n\n" + "%(namespace_prefix)s" + "// Returns an initialized UIStringOverrider.\n" + "variations::UIStringOverrider CreateUIStringOverrider();\n" + "%(namespace_suffix)s" + "\n" + "#endif // %(include_guard)s\n" + ) % { + 'script_name': SCRIPT_NAME, + 'include_guard': include_guard, + 'namespace_prefix': namespace_prefix, + 'namespace_suffix': namespace_suffix, + } + + +def main(): + arg_parser = argparse.ArgumentParser( + description="Generate UIStringOverrider factory from resources headers " + "generated by grit.") + arg_parser.add_argument( + "--output_dir", "-o", required=True, + help="Base directory to for generated files.") + arg_parser.add_argument( + "--source_filename", "-S", required=True, + help="File name of the generated source file.") + arg_parser.add_argument( + "--header_filename", "-H", required=True, + help="File name of the generated header file.") + arg_parser.add_argument( + "--namespace", "-N", default="", + help="Namespace of the generated factory function (code will be in " + "the global namespace if this is omitted).") + arg_parser.add_argument( + "--test_support", "-t", action="store_true", default=False, + help="Make internal variables accessible for testing.") + arg_parser.add_argument( + "inputs", metavar="FILENAME", nargs="+", + help="Path to resources header file generated by grit.") + arguments = arg_parser.parse_args() + + generated_resources_h = "" + for resources_file in arguments.inputs: + with open(resources_file, "r") as resources: + generated_resources_h += resources.read() + + if len(generated_resources_h) == 0: + raise Error("No content loaded for %s." % (resources_file)) + + source_file_content = _GenerateSourceFileContent( + generated_resources_h, arguments.namespace, arguments.header_filename) + header_file_content = _GenerateHeaderFileContent( + arguments.namespace, arguments.header_filename) + + with open(os.path.join( + arguments.output_dir, arguments.source_filename), "w") as generated_file: + generated_file.write(source_file_content) + with open(os.path.join( + arguments.output_dir, arguments.header_filename), "w") as generated_file: + generated_file.write(header_file_content) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/chrome/browser/metrics/variations/generate_resources_map_unittest.py b/components/variations/service/generate_ui_string_overrider_unittest.py index a7b1a2b..55d55f3 100755 --- a/chrome/browser/metrics/variations/generate_resources_map_unittest.py +++ b/components/variations/service/generate_ui_string_overrider_unittest.py @@ -3,14 +3,16 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Unittests for generate_resources_map.py""" +"""Unittests for generate_ui_string_overrider.py""" import unittest -import generate_resources_map +import generate_ui_string_overrider class GenerateResourcesMapUnittest(unittest.TestCase): + NAMESPACE = "chrome_variations" + OUT_HEADER = "components/variations/service/ui_string_overrider_factory.h" TEST_INPUT = """ // This file is automatically generated by GRIT. Do not edit. @@ -27,9 +29,10 @@ class GenerateResourcesMapUnittest(unittest.TestCase): (2654138887, "IDS_BOOKMARK_BAR_IMPORT_LINK", "12501"), (2894469061, "IDS_BOOKMARK_GROUP_FROM_IE", "12502"), (3847176170, "IDS_BOOKMARK_GROUP_FROM_FIREFOX", "12503")] - expected = [generate_resources_map.Resource(*t) for t in expected_tuples] + expected = [ + generate_ui_string_overrider.Resource(*t) for t in expected_tuples] - actual_tuples = generate_resources_map._GetResourceListFromString( + actual_tuples = generate_ui_string_overrider._GetResourceListFromString( self.TEST_INPUT) self.assertEqual(expected_tuples, actual_tuples) @@ -42,31 +45,34 @@ class GenerateResourcesMapUnittest(unittest.TestCase): (890, "IDS_QUX", "12503"), (899, "IDS_NO", "12504"), (899, "IDS_YES", "12505")] - list_with_collisions = [generate_resources_map.Resource(*t) + list_with_collisions = [generate_ui_string_overrider.Resource(*t) for t in collisions_tuples] expected_collision_tuples = [(456, "IDS_BAR", "12501"), (456, "IDS_BAZ", "12502"), (899, "IDS_NO", "12504"), (899, "IDS_YES", "12505")] - expected_collisions = [generate_resources_map.Resource(*t) + expected_collisions = [generate_ui_string_overrider.Resource(*t) for t in expected_collision_tuples] actual_collisions = sorted( - generate_resources_map._CheckForHashCollisions(list_with_collisions)) + generate_ui_string_overrider._CheckForHashCollisions( + list_with_collisions)) actual_collisions self.assertEqual(expected_collisions, actual_collisions) - def testGenerateFileContent(self): + def testGenerateSourceFileContent(self): expected = ( - """// This file was generated by generate_resources_map.py. Do not edit. + """\ +// This file was generated by generate_ui_string_overrider.py. Do not edit. - -#include "chrome/browser/metrics/variations/generated_resources_map.h" +#include "components/variations/service/ui_string_overrider_factory.h" namespace chrome_variations { +namespace { + const size_t kNumResources = 4; const uint32_t kResourceHashes[] = { @@ -83,9 +89,42 @@ const int kResourceIndices[] = { 12503, // IDS_BOOKMARK_GROUP_FROM_FIREFOX }; +} // namespace + +variations::UIStringOverrider CreateUIStringOverrider() { + return variations::UIStringOverrider( + kResourceHashes, kResourceIndices, kNumResources); +} + +} // namespace chrome_variations +""") + actual = generate_ui_string_overrider._GenerateSourceFileContent( + self.TEST_INPUT, self.NAMESPACE, self.OUT_HEADER) + + self.assertEqual(expected, actual) + + + def testGenerateHeaderFileContent(self): + expected = ( + """\ +// This file was generated by generate_ui_string_overrider.py. Do not edit. + +#ifndef COMPONENTS_VARIATIONS_SERVICE_UI_STRING_OVERRIDER_FACTORY_H_ +#define COMPONENTS_VARIATIONS_SERVICE_UI_STRING_OVERRIDER_FACTORY_H_ + +#include "components/variations/service/ui_string_overrider.h" + +namespace chrome_variations { + +// Returns an initialized UIStringOverrider. +variations::UIStringOverrider CreateUIStringOverrider(); + } // namespace chrome_variations + +#endif // COMPONENTS_VARIATIONS_SERVICE_UI_STRING_OVERRIDER_FACTORY_H_ """) - actual = generate_resources_map._GenerateFileContent(self.TEST_INPUT) + actual = generate_ui_string_overrider._GenerateHeaderFileContent( + self.NAMESPACE, self.OUT_HEADER) self.assertEqual(expected, actual) diff --git a/components/variations/service/ui_string_overrider.h b/components/variations/service/ui_string_overrider.h index 5908444..8aa4784 100644 --- a/components/variations/service/ui_string_overrider.h +++ b/components/variations/service/ui_string_overrider.h @@ -16,7 +16,7 @@ namespace variations { // array of resource name hashes, and |resource_indices| an array of resource // indices in the same order. // -// The mapping is created by generate_resources_map.py based on generated +// The mapping is created by generate_ui_string_overrider.py based on generated // resources header files. The script ensure that if one header file contains // |#define IDS_FOO 12345| then for some index |i|, |resource_hashes[i]| will // be equal to |HASH("IDS_FOO")| and |resource_indices[i]| will be equal to diff --git a/chrome/browser/metrics/variations/ui_string_overrider_unittest.cc b/components/variations/service/ui_string_overrider_unittest.cc index 5aace0e..640d51c 100644 --- a/chrome/browser/metrics/variations/ui_string_overrider_unittest.cc +++ b/components/variations/service/ui_string_overrider_unittest.cc @@ -4,15 +4,30 @@ #include "components/variations/service/ui_string_overrider.h" -#include "chrome/browser/metrics/variations/generated_resources_map.h" #include "testing/gtest/include/gtest/gtest.h" -// TODO(sdefresne): componentize this tests as part of the resolution -// of http://crbug.com/534257 ("Decouple iOS port from using //chrome's -// VariationsService client"). - namespace chrome_variations { +namespace { + +const size_t kNumResources = 4; + +const uint32_t kResourceHashes[] = { + 301430091U, // IDS_BOOKMARKS_NO_ITEMS + 2654138887U, // IDS_BOOKMARK_BAR_IMPORT_LINK + 2894469061U, // IDS_BOOKMARK_GROUP_FROM_IE + 3847176170U, // IDS_BOOKMARK_GROUP_FROM_FIREFOX +}; + +const int kResourceIndices[] = { + 12500, // IDS_BOOKMARKS_NO_ITEMS + 12501, // IDS_BOOKMARK_BAR_IMPORT_LINK + 12502, // IDS_BOOKMARK_GROUP_FROM_IE + 12503, // IDS_BOOKMARK_GROUP_FROM_FIREFOX +}; + +} // namespace + class UIStringOverriderTest : public ::testing::Test { public: UIStringOverriderTest() @@ -33,8 +48,8 @@ TEST_F(UIStringOverriderTest, LookupNotFound) { EXPECT_EQ(-1, GetResourceIndex(kResourceHashes[kNumResources - 1] + 1)); // Lookup a hash that shouldn't exist. - // 13257681U is 1 + the hash for IDS_ZOOM_NORMAL - EXPECT_EQ(-1, GetResourceIndex(13257681U)); + // 3847176171U is 1 + the hash for IDS_BOOKMARK_GROUP_FROM_FIREFOX. + EXPECT_EQ(-1, GetResourceIndex(3847176171U)); } TEST_F(UIStringOverriderTest, LookupFound) { |