summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsdefresne <sdefresne@chromium.org>2015-10-05 02:23:39 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-05 09:24:34 +0000
commita213cebe6af1a80367336908d37f30ab6542940d (patch)
treef48f639295d3d4d5afa4f2346be26f6e412765ad
parentdba5fc94c1c009edef581ed788f954c8c980ee52 (diff)
downloadchromium_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}
-rw-r--r--chrome/app/BUILD.gn32
-rw-r--r--chrome/browser/BUILD.gn4
-rw-r--r--chrome/browser/metrics/metrics_services_manager.cc6
-rw-r--r--chrome/browser/metrics/variations/BUILD.gn18
-rwxr-xr-xchrome/browser/metrics/variations/generate_resources_map.py183
-rw-r--r--chrome/browser/metrics/variations/generated_resources_map.h35
-rw-r--r--chrome/chrome_browser.gypi8
-rw-r--r--chrome/chrome_resources.gyp32
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/variations/service/BUILD.gn1
-rw-r--r--components/variations/service/generate_ui_string_overrider.gni66
-rwxr-xr-xcomponents/variations/service/generate_ui_string_overrider.py292
-rwxr-xr-xcomponents/variations/service/generate_ui_string_overrider_unittest.py (renamed from chrome/browser/metrics/variations/generate_resources_map_unittest.py)63
-rw-r--r--components/variations/service/ui_string_overrider.h2
-rw-r--r--components/variations/service/ui_string_overrider_unittest.cc (renamed from chrome/browser/metrics/variations/ui_string_overrider_unittest.cc)29
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) {