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

Unified Diff: chrome/common/extensions/docs/build/directory.py

Issue 4106007: Only generate zips for Chrome extension samples whose contents have changed. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/chrome/common/extensions/docs
Patch Set: Reapply patch after unicode fixes. Created 10 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: chrome/common/extensions/docs/build/directory.py
diff --git a/chrome/common/extensions/docs/build/directory.py b/chrome/common/extensions/docs/build/directory.py
index 1c7cb757f80deec00846c81701199b30139e3c01..f95344fcb099a27b46e54c358f24ac5548d29a5e 100644
--- a/chrome/common/extensions/docs/build/directory.py
+++ b/chrome/common/extensions/docs/build/directory.py
@@ -225,8 +225,7 @@ class SamplesManifest(object):
Args:
path: The path to write the samples manifest file to.
"""
-
- manifest_text = json.dumps(self._manifest_data, indent=2)
+ manifest_text = json.dumps(self._manifest_data, indent=2, sort_keys=True)
output_path = os.path.realpath(path)
try:
output_file = open(output_path, 'w')
@@ -238,10 +237,18 @@ class SamplesManifest(object):
def writeZippedSamples(self):
""" For each sample in the current manifest, create a zip file with the
- sample contents in the sample's parent directory. """
+ sample contents in the sample's parent directory if not zip exists, or
+ update the zip file if the sample has been updated.
+ Returns:
+ A set of paths representing zip files which have been modified.
+ """
+ modified_paths = []
for sample in self._manifest_data['samples']:
- sample.write_zip()
+ path = sample.write_zip()
+ if path:
+ modified_paths.append(path)
+ return modified_paths
class Sample(dict):
""" Represents metadata about a Chrome extension sample.
@@ -264,10 +271,13 @@ class Sample(dict):
self._manifest = parse_json_file(self._manifest_path)
self._locale_data = self._parse_locale_data()
- # The following properties will be serialized when converting this object
- # to JSON.
+ # The following calls set data which will be serialized when converting
+ # this object to JSON.
+ source_data = self._parse_source_data(api_methods)
+ self['api_calls'] = source_data['api_calls']
+ self['source_files'] = source_data['source_files']
+ self['source_hash'] = source_data['source_hash']
- self['api_calls'] = self._parse_api_calls(api_methods)
self['name'] = self._parse_name()
self['description'] = self._parse_description()
self['icon'] = self._parse_icon()
@@ -275,7 +285,6 @@ class Sample(dict):
self['protocols'] = self._parse_protocols()
self['path'] = self._get_relative_path()
self['search_string'] = self._get_search_string()
- self['source_files'] = self._parse_source_files()
self['id'] = hashlib.sha1(self['path']).hexdigest()
self['zip_path'] = self._get_relative_zip_path()
@@ -421,62 +430,6 @@ class Sample(dict):
sample_dirname = os.path.basename(sample_path)
return "%s.zip" % sample_dirname
- def _parse_api_calls(self, api_methods):
- """ Returns a list of Chrome extension API calls the sample makes.
-
- Parses any *.html and *.js files in the sample directory and matches them
- against the supplied list of all available API methods, returning methods
- which show up in the sample code.
-
- Args:
- api_methods: A list of strings containing the potential
- API calls the and the extension sample could be making.
-
- Returns:
- A set of every member of api_methods that appears in any *.html or *.js
- file contained in this sample's directory (or subdirectories).
-
- Raises:
- Exception: If any of the *.html or *.js files cannot be read.
- """
- api_calls = set()
- extension_dir_path = os.path.dirname(self._manifest_path)
- for root, dirs, files in sorted_walk(extension_dir_path):
- for file in files:
- if file[-5:] == '.html' or file[-3:] == '.js':
- path = os.path.join(root, file)
- try:
- code_file = open(path, "r")
- except IOError, msg:
- raise Exception("Failed to read %s: %s" % (path, msg))
- code_contents = unicode(code_file.read(), errors="replace")
- code_file.close()
-
- for method in api_methods:
- if (code_contents.find(method) > -1):
- api_calls.add(method)
- return sorted(api_calls)
-
- def _parse_source_files(self):
- """ Returns a list of paths to source files present in the extenion.
-
- Returns:
- A list of paths relative to the manifest file directory.
- """
- source_paths = []
- base_path = os.path.realpath(os.path.dirname(self._manifest_path))
- for root, directories, files in sorted_walk(base_path):
- if '.svn' in directories:
- directories.remove('.svn') # Don't go into SVN metadata directories
-
- for file_name in files:
- ext = os.path.splitext(file_name)[1]
- if ext in self._SOURCE_FILE_EXTENSIONS:
- path = os.path.realpath(os.path.join(root, file_name))
- path = path.replace(base_path, '')[1:]
- source_paths.append(path)
- return sorted(source_paths)
-
def _parse_description(self):
""" Returns a localized description of the extension.
@@ -577,6 +530,66 @@ class Sample(dict):
protocols.append(split[0] + "://")
return protocols
+ def _parse_source_data(self, api_methods):
+ """ Iterates over the sample's source files and parses data from them.
+
+ Parses any files in the sample directory with known source extensions
+ (as defined in self._SOURCE_FILE_EXTENSIONS). For each file, this method:
+
+ 1. Stores a relative path from the manifest.json directory to the file.
+ 2. Searches through the contents of the file for chrome.* API calls.
+ 3. Calculates a SHA1 digest for the contents of the file.
+
+ Args:
+ api_methods: A list of strings containing the potential
+ API calls the and the extension sample could be making.
+
+ Raises:
+ Exception: If any of the source files cannot be read.
+
+ Returns:
+ A dictionary containing the keys/values:
+ 'api_calls' A sorted list of API calls the sample makes.
+ 'source_files' A sorted list of paths to files the extension uses.
+ 'source_hash' A hash of the individual file hashes.
+ """
+ data = {}
+ source_paths = []
+ source_hashes = []
+ api_calls = set()
+ base_path = os.path.realpath(os.path.dirname(self._manifest_path))
+ for root, directories, files in sorted_walk(base_path):
+ if '.svn' in directories:
+ directories.remove('.svn') # Don't go into SVN metadata directories
+
+ for file_name in files:
+ ext = os.path.splitext(file_name)[1]
+ if ext in self._SOURCE_FILE_EXTENSIONS:
+ # Add the file path to the list of source paths.
+ fullpath = os.path.realpath(os.path.join(root, file_name))
+ path = fullpath.replace(base_path, '')[1:]
+ source_paths.append(path)
+
+ # Read the contents and parse out API calls.
+ try:
+ code_file = open(fullpath, "r")
+ except IOError, msg:
+ raise Exception("Failed to read %s: %s" % (fullpath, msg))
+ code_contents = unicode(code_file.read(), errors="replace")
+ code_file.close()
+ for method in api_methods:
+ if (code_contents.find(method) > -1):
+ api_calls.add(method)
+
+ # Get a hash of the file contents for zip file generation.
+ hash = hashlib.sha1(code_contents.encode("ascii", "replace"))
+ source_hashes.append(hash.hexdigest())
+
+ data['api_calls'] = sorted(api_calls)
+ data['source_files'] = sorted(source_paths)
+ data['source_hash'] = hashlib.sha1(''.join(source_hashes)).hexdigest()
+ return data
+
def _uses_background(self):
""" Returns true if the extension defines a background page. """
return self._manifest.has_key('background_page')
@@ -618,6 +631,26 @@ class Sample(dict):
zip_filename = self._get_zip_filename()
zip_path = os.path.join(sample_parentpath, zip_filename)
+ zip_manifest_path = os.path.join(sample_dirname, 'manifest.json')
+
+ zipfile.ZipFile.debug = 3
+
+ if os.path.isfile(zip_path):
+ try:
+ old_zip_file = zipfile.ZipFile(zip_path, 'r')
+ except IOError, msg:
+ raise Exception("Could not read zip at %s: %s" % (zip_path, msg))
+
+ try:
+ info = old_zip_file.getinfo(zip_manifest_path)
+ hash = info.comment
+ if hash == self['source_hash']:
+ return None # Hashes match - no need to generate file
+ except KeyError, msg:
+ pass # The old zip file doesn't contain a hash - overwrite
+ finally:
+ old_zip_file.close()
+
zip_file = zipfile.ZipFile(zip_path, 'w')
try:
@@ -629,8 +662,14 @@ class Sample(dict):
abspath = os.path.realpath(os.path.join(root, file))
# Relative path to store the file in under the zip.
relpath = sample_dirname + abspath.replace(sample_path, "")
+
zip_file.write(abspath, relpath)
+ if file == 'manifest.json':
+ info = zip_file.getinfo(zip_manifest_path)
+ info.comment = self['source_hash']
except RuntimeError, msg:
- raise Exception("Could not write zip at " % zip_path)
+ raise Exception("Could not write zip at %s: %s" % (zip_path, msg))
finally:
zip_file.close()
+
+ return self._get_relative_zip_path()
« no previous file with comments | « chrome/common/extensions/docs/build/build.py ('k') | chrome/common/extensions/docs/examples/api/bookmarks/basic.zip » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698