redsnow 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.gitmodules +3 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/Rakefile +36 -0
- data/Vagrantfile +20 -0
- data/ext/snowcrash/Makefile +64 -0
- data/ext/snowcrash/Vagrantfile +20 -0
- data/ext/snowcrash/bin/snowcrash +0 -0
- data/ext/snowcrash/common.gypi +163 -0
- data/ext/snowcrash/config.gypi +10 -0
- data/ext/snowcrash/config.mk +5 -0
- data/ext/snowcrash/configure +213 -0
- data/ext/snowcrash/provisioning.sh +15 -0
- data/ext/snowcrash/snowcrash.gyp +141 -0
- data/ext/snowcrash/src/ActionParser.h +503 -0
- data/ext/snowcrash/src/AssetParser.h +215 -0
- data/ext/snowcrash/src/BlockUtility.h +186 -0
- data/ext/snowcrash/src/Blueprint.h +283 -0
- data/ext/snowcrash/src/BlueprintParser.h +347 -0
- data/ext/snowcrash/src/BlueprintParserCore.h +190 -0
- data/ext/snowcrash/src/BlueprintSection.h +140 -0
- data/ext/snowcrash/src/BlueprintUtility.h +126 -0
- data/ext/snowcrash/src/CBlueprint.cc +600 -0
- data/ext/snowcrash/src/CBlueprint.h +354 -0
- data/ext/snowcrash/src/CSourceAnnotation.cc +140 -0
- data/ext/snowcrash/src/CSourceAnnotation.h +106 -0
- data/ext/snowcrash/src/CodeBlockUtility.h +189 -0
- data/ext/snowcrash/src/DescriptionSectionUtility.h +156 -0
- data/ext/snowcrash/src/HTTP.cc +46 -0
- data/ext/snowcrash/src/HTTP.h +105 -0
- data/ext/snowcrash/src/HeaderParser.h +289 -0
- data/ext/snowcrash/src/ListBlockUtility.h +273 -0
- data/ext/snowcrash/src/ListUtility.h +95 -0
- data/ext/snowcrash/src/MarkdownBlock.cc +176 -0
- data/ext/snowcrash/src/MarkdownBlock.h +93 -0
- data/ext/snowcrash/src/MarkdownParser.cc +266 -0
- data/ext/snowcrash/src/MarkdownParser.h +88 -0
- data/ext/snowcrash/src/ParameterDefinitonParser.h +570 -0
- data/ext/snowcrash/src/ParametersParser.h +252 -0
- data/ext/snowcrash/src/Parser.cc +71 -0
- data/ext/snowcrash/src/Parser.h +29 -0
- data/ext/snowcrash/src/ParserCore.cc +120 -0
- data/ext/snowcrash/src/ParserCore.h +82 -0
- data/ext/snowcrash/src/PayloadParser.h +672 -0
- data/ext/snowcrash/src/Platform.h +54 -0
- data/ext/snowcrash/src/RegexMatch.h +32 -0
- data/ext/snowcrash/src/ResourceGroupParser.h +195 -0
- data/ext/snowcrash/src/ResourceParser.h +584 -0
- data/ext/snowcrash/src/SectionUtility.h +142 -0
- data/ext/snowcrash/src/Serialize.cc +52 -0
- data/ext/snowcrash/src/Serialize.h +69 -0
- data/ext/snowcrash/src/SerializeJSON.cc +601 -0
- data/ext/snowcrash/src/SerializeJSON.h +21 -0
- data/ext/snowcrash/src/SerializeYAML.cc +336 -0
- data/ext/snowcrash/src/SerializeYAML.h +21 -0
- data/ext/snowcrash/src/SourceAnnotation.h +177 -0
- data/ext/snowcrash/src/StringUtility.h +109 -0
- data/ext/snowcrash/src/SymbolTable.h +83 -0
- data/ext/snowcrash/src/UriTemplateParser.cc +195 -0
- data/ext/snowcrash/src/UriTemplateParser.h +243 -0
- data/ext/snowcrash/src/Version.h +39 -0
- data/ext/snowcrash/src/csnowcrash.cc +23 -0
- data/ext/snowcrash/src/csnowcrash.h +38 -0
- data/ext/snowcrash/src/posix/RegexMatch.cc +99 -0
- data/ext/snowcrash/src/snowcrash.cc +18 -0
- data/ext/snowcrash/src/snowcrash.h +41 -0
- data/ext/snowcrash/src/snowcrash/snowcrash.cc +170 -0
- data/ext/snowcrash/src/win/RegexMatch.cc +78 -0
- data/ext/snowcrash/sundown/CONTRIBUTING.md +10 -0
- data/ext/snowcrash/sundown/Makefile +83 -0
- data/ext/snowcrash/sundown/Makefile.win +33 -0
- data/ext/snowcrash/sundown/examples/smartypants.c +72 -0
- data/ext/snowcrash/sundown/examples/sundown.c +80 -0
- data/ext/snowcrash/sundown/html/houdini.h +37 -0
- data/ext/snowcrash/sundown/html/houdini_href_e.c +108 -0
- data/ext/snowcrash/sundown/html/houdini_html_e.c +84 -0
- data/ext/snowcrash/sundown/html/html.c +647 -0
- data/ext/snowcrash/sundown/html/html.h +77 -0
- data/ext/snowcrash/sundown/html/html_smartypants.c +389 -0
- data/ext/snowcrash/sundown/html_block_names.txt +25 -0
- data/ext/snowcrash/sundown/src/autolink.c +297 -0
- data/ext/snowcrash/sundown/src/autolink.h +51 -0
- data/ext/snowcrash/sundown/src/buffer.c +225 -0
- data/ext/snowcrash/sundown/src/buffer.h +96 -0
- data/ext/snowcrash/sundown/src/html_blocks.h +206 -0
- data/ext/snowcrash/sundown/src/markdown.c +2701 -0
- data/ext/snowcrash/sundown/src/markdown.h +147 -0
- data/ext/snowcrash/sundown/src/src_map.c +200 -0
- data/ext/snowcrash/sundown/src/src_map.h +58 -0
- data/ext/snowcrash/sundown/src/stack.c +81 -0
- data/ext/snowcrash/sundown/src/stack.h +29 -0
- data/ext/snowcrash/sundown/sundown.def +20 -0
- data/ext/snowcrash/tools/gyp/AUTHORS +11 -0
- data/ext/snowcrash/tools/gyp/DEPS +24 -0
- data/ext/snowcrash/tools/gyp/OWNERS +1 -0
- data/ext/snowcrash/tools/gyp/PRESUBMIT.py +120 -0
- data/ext/snowcrash/tools/gyp/buildbot/buildbot_run.py +190 -0
- data/ext/snowcrash/tools/gyp/codereview.settings +10 -0
- data/ext/snowcrash/tools/gyp/data/win/large-pdb-shim.cc +12 -0
- data/ext/snowcrash/tools/gyp/gyp +8 -0
- data/ext/snowcrash/tools/gyp/gyp.bat +5 -0
- data/ext/snowcrash/tools/gyp/gyp_main.py +18 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSNew.py +340 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSProject.py +208 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSSettings.py +1063 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSToolFile.py +58 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUserFile.py +147 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUtil.py +267 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSVersion.py +409 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.py +537 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/common.py +521 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/common.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/easy_xml.py +157 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/flock_tool.py +49 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.py +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/android.py +1069 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/cmake.py +1143 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/dump_dependency_json.py +81 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/eclipse.py +335 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypd.py +87 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypsh.py +56 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.py +2181 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/msvs.py +3335 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/ninja.py +2156 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.py +1224 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/input.py +2809 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/input.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/mac_tool.py +510 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/msvs_emulation.py +972 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/ninja_syntax.py +160 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/ordered_dict.py +289 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/win_tool.py +292 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.py +1440 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.py +2889 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xml_fix.py +69 -0
- data/ext/snowcrash/tools/gyp/pylintrc +307 -0
- data/ext/snowcrash/tools/gyp/samples/samples +81 -0
- data/ext/snowcrash/tools/gyp/samples/samples.bat +5 -0
- data/ext/snowcrash/tools/gyp/setup.py +19 -0
- data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.pbfilespec +27 -0
- data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.xclangspec +226 -0
- data/ext/snowcrash/tools/gyp/tools/emacs/gyp.el +252 -0
- data/ext/snowcrash/tools/gyp/tools/graphviz.py +100 -0
- data/ext/snowcrash/tools/gyp/tools/pretty_gyp.py +155 -0
- data/ext/snowcrash/tools/gyp/tools/pretty_sln.py +168 -0
- data/ext/snowcrash/tools/gyp/tools/pretty_vcproj.py +329 -0
- data/ext/snowcrash/tools/homebrew/snowcrash.rb +11 -0
- data/ext/snowcrash/vcbuild.bat +184 -0
- data/lib/redsnow.rb +31 -0
- data/lib/redsnow/binding.rb +132 -0
- data/lib/redsnow/blueprint.rb +365 -0
- data/lib/redsnow/object.rb +18 -0
- data/lib/redsnow/parseresult.rb +107 -0
- data/lib/redsnow/version.rb +4 -0
- data/provisioning.sh +20 -0
- data/redsnow.gemspec +35 -0
- data/test/_helper.rb +15 -0
- data/test/fixtures/sample-api-ast.json +97 -0
- data/test/fixtures/sample-api.apib +20 -0
- data/test/redsnow_binding_test.rb +35 -0
- data/test/redsnow_parseresult_test.rb +50 -0
- data/test/redsnow_test.rb +285 -0
- metadata +358 -0
Binary file
|
@@ -0,0 +1,521 @@
|
|
1
|
+
# Copyright (c) 2012 Google Inc. 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
|
+
from __future__ import with_statement
|
6
|
+
|
7
|
+
import errno
|
8
|
+
import filecmp
|
9
|
+
import os.path
|
10
|
+
import re
|
11
|
+
import tempfile
|
12
|
+
import sys
|
13
|
+
|
14
|
+
|
15
|
+
# A minimal memoizing decorator. It'll blow up if the args aren't immutable,
|
16
|
+
# among other "problems".
|
17
|
+
class memoize(object):
|
18
|
+
def __init__(self, func):
|
19
|
+
self.func = func
|
20
|
+
self.cache = {}
|
21
|
+
def __call__(self, *args):
|
22
|
+
try:
|
23
|
+
return self.cache[args]
|
24
|
+
except KeyError:
|
25
|
+
result = self.func(*args)
|
26
|
+
self.cache[args] = result
|
27
|
+
return result
|
28
|
+
|
29
|
+
|
30
|
+
class GypError(Exception):
|
31
|
+
"""Error class representing an error, which is to be presented
|
32
|
+
to the user. The main entry point will catch and display this.
|
33
|
+
"""
|
34
|
+
pass
|
35
|
+
|
36
|
+
|
37
|
+
def ExceptionAppend(e, msg):
|
38
|
+
"""Append a message to the given exception's message."""
|
39
|
+
if not e.args:
|
40
|
+
e.args = (msg,)
|
41
|
+
elif len(e.args) == 1:
|
42
|
+
e.args = (str(e.args[0]) + ' ' + msg,)
|
43
|
+
else:
|
44
|
+
e.args = (str(e.args[0]) + ' ' + msg,) + e.args[1:]
|
45
|
+
|
46
|
+
|
47
|
+
def FindQualifiedTargets(target, qualified_list):
|
48
|
+
"""
|
49
|
+
Given a list of qualified targets, return the qualified targets for the
|
50
|
+
specified |target|.
|
51
|
+
"""
|
52
|
+
return [t for t in qualified_list if ParseQualifiedTarget(t)[1] == target]
|
53
|
+
|
54
|
+
|
55
|
+
def ParseQualifiedTarget(target):
|
56
|
+
# Splits a qualified target into a build file, target name and toolset.
|
57
|
+
|
58
|
+
# NOTE: rsplit is used to disambiguate the Windows drive letter separator.
|
59
|
+
target_split = target.rsplit(':', 1)
|
60
|
+
if len(target_split) == 2:
|
61
|
+
[build_file, target] = target_split
|
62
|
+
else:
|
63
|
+
build_file = None
|
64
|
+
|
65
|
+
target_split = target.rsplit('#', 1)
|
66
|
+
if len(target_split) == 2:
|
67
|
+
[target, toolset] = target_split
|
68
|
+
else:
|
69
|
+
toolset = None
|
70
|
+
|
71
|
+
return [build_file, target, toolset]
|
72
|
+
|
73
|
+
|
74
|
+
def ResolveTarget(build_file, target, toolset):
|
75
|
+
# This function resolves a target into a canonical form:
|
76
|
+
# - a fully defined build file, either absolute or relative to the current
|
77
|
+
# directory
|
78
|
+
# - a target name
|
79
|
+
# - a toolset
|
80
|
+
#
|
81
|
+
# build_file is the file relative to which 'target' is defined.
|
82
|
+
# target is the qualified target.
|
83
|
+
# toolset is the default toolset for that target.
|
84
|
+
[parsed_build_file, target, parsed_toolset] = ParseQualifiedTarget(target)
|
85
|
+
|
86
|
+
if parsed_build_file:
|
87
|
+
if build_file:
|
88
|
+
# If a relative path, parsed_build_file is relative to the directory
|
89
|
+
# containing build_file. If build_file is not in the current directory,
|
90
|
+
# parsed_build_file is not a usable path as-is. Resolve it by
|
91
|
+
# interpreting it as relative to build_file. If parsed_build_file is
|
92
|
+
# absolute, it is usable as a path regardless of the current directory,
|
93
|
+
# and os.path.join will return it as-is.
|
94
|
+
build_file = os.path.normpath(os.path.join(os.path.dirname(build_file),
|
95
|
+
parsed_build_file))
|
96
|
+
# Further (to handle cases like ../cwd), make it relative to cwd)
|
97
|
+
if not os.path.isabs(build_file):
|
98
|
+
build_file = RelativePath(build_file, '.')
|
99
|
+
else:
|
100
|
+
build_file = parsed_build_file
|
101
|
+
|
102
|
+
if parsed_toolset:
|
103
|
+
toolset = parsed_toolset
|
104
|
+
|
105
|
+
return [build_file, target, toolset]
|
106
|
+
|
107
|
+
|
108
|
+
def BuildFile(fully_qualified_target):
|
109
|
+
# Extracts the build file from the fully qualified target.
|
110
|
+
return ParseQualifiedTarget(fully_qualified_target)[0]
|
111
|
+
|
112
|
+
|
113
|
+
def GetEnvironFallback(var_list, default):
|
114
|
+
"""Look up a key in the environment, with fallback to secondary keys
|
115
|
+
and finally falling back to a default value."""
|
116
|
+
for var in var_list:
|
117
|
+
if var in os.environ:
|
118
|
+
return os.environ[var]
|
119
|
+
return default
|
120
|
+
|
121
|
+
|
122
|
+
def QualifiedTarget(build_file, target, toolset):
|
123
|
+
# "Qualified" means the file that a target was defined in and the target
|
124
|
+
# name, separated by a colon, suffixed by a # and the toolset name:
|
125
|
+
# /path/to/file.gyp:target_name#toolset
|
126
|
+
fully_qualified = build_file + ':' + target
|
127
|
+
if toolset:
|
128
|
+
fully_qualified = fully_qualified + '#' + toolset
|
129
|
+
return fully_qualified
|
130
|
+
|
131
|
+
|
132
|
+
@memoize
|
133
|
+
def RelativePath(path, relative_to):
|
134
|
+
# Assuming both |path| and |relative_to| are relative to the current
|
135
|
+
# directory, returns a relative path that identifies path relative to
|
136
|
+
# relative_to.
|
137
|
+
|
138
|
+
# Convert to normalized (and therefore absolute paths).
|
139
|
+
path = os.path.realpath(path)
|
140
|
+
relative_to = os.path.realpath(relative_to)
|
141
|
+
|
142
|
+
# On Windows, we can't create a relative path to a different drive, so just
|
143
|
+
# use the absolute path.
|
144
|
+
if sys.platform == 'win32':
|
145
|
+
if (os.path.splitdrive(path)[0].lower() !=
|
146
|
+
os.path.splitdrive(relative_to)[0].lower()):
|
147
|
+
return path
|
148
|
+
|
149
|
+
# Split the paths into components.
|
150
|
+
path_split = path.split(os.path.sep)
|
151
|
+
relative_to_split = relative_to.split(os.path.sep)
|
152
|
+
|
153
|
+
# Determine how much of the prefix the two paths share.
|
154
|
+
prefix_len = len(os.path.commonprefix([path_split, relative_to_split]))
|
155
|
+
|
156
|
+
# Put enough ".." components to back up out of relative_to to the common
|
157
|
+
# prefix, and then append the part of path_split after the common prefix.
|
158
|
+
relative_split = [os.path.pardir] * (len(relative_to_split) - prefix_len) + \
|
159
|
+
path_split[prefix_len:]
|
160
|
+
|
161
|
+
if len(relative_split) == 0:
|
162
|
+
# The paths were the same.
|
163
|
+
return ''
|
164
|
+
|
165
|
+
# Turn it back into a string and we're done.
|
166
|
+
return os.path.join(*relative_split)
|
167
|
+
|
168
|
+
|
169
|
+
@memoize
|
170
|
+
def InvertRelativePath(path, toplevel_dir=None):
|
171
|
+
"""Given a path like foo/bar that is relative to toplevel_dir, return
|
172
|
+
the inverse relative path back to the toplevel_dir.
|
173
|
+
|
174
|
+
E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
|
175
|
+
should always produce the empty string, unless the path contains symlinks.
|
176
|
+
"""
|
177
|
+
if not path:
|
178
|
+
return path
|
179
|
+
toplevel_dir = '.' if toplevel_dir is None else toplevel_dir
|
180
|
+
return RelativePath(toplevel_dir, os.path.join(toplevel_dir, path))
|
181
|
+
|
182
|
+
|
183
|
+
def FixIfRelativePath(path, relative_to):
|
184
|
+
# Like RelativePath but returns |path| unchanged if it is absolute.
|
185
|
+
if os.path.isabs(path):
|
186
|
+
return path
|
187
|
+
return RelativePath(path, relative_to)
|
188
|
+
|
189
|
+
|
190
|
+
def UnrelativePath(path, relative_to):
|
191
|
+
# Assuming that |relative_to| is relative to the current directory, and |path|
|
192
|
+
# is a path relative to the dirname of |relative_to|, returns a path that
|
193
|
+
# identifies |path| relative to the current directory.
|
194
|
+
rel_dir = os.path.dirname(relative_to)
|
195
|
+
return os.path.normpath(os.path.join(rel_dir, path))
|
196
|
+
|
197
|
+
|
198
|
+
# re objects used by EncodePOSIXShellArgument. See IEEE 1003.1 XCU.2.2 at
|
199
|
+
# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_02
|
200
|
+
# and the documentation for various shells.
|
201
|
+
|
202
|
+
# _quote is a pattern that should match any argument that needs to be quoted
|
203
|
+
# with double-quotes by EncodePOSIXShellArgument. It matches the following
|
204
|
+
# characters appearing anywhere in an argument:
|
205
|
+
# \t, \n, space parameter separators
|
206
|
+
# # comments
|
207
|
+
# $ expansions (quoted to always expand within one argument)
|
208
|
+
# % called out by IEEE 1003.1 XCU.2.2
|
209
|
+
# & job control
|
210
|
+
# ' quoting
|
211
|
+
# (, ) subshell execution
|
212
|
+
# *, ?, [ pathname expansion
|
213
|
+
# ; command delimiter
|
214
|
+
# <, >, | redirection
|
215
|
+
# = assignment
|
216
|
+
# {, } brace expansion (bash)
|
217
|
+
# ~ tilde expansion
|
218
|
+
# It also matches the empty string, because "" (or '') is the only way to
|
219
|
+
# represent an empty string literal argument to a POSIX shell.
|
220
|
+
#
|
221
|
+
# This does not match the characters in _escape, because those need to be
|
222
|
+
# backslash-escaped regardless of whether they appear in a double-quoted
|
223
|
+
# string.
|
224
|
+
_quote = re.compile('[\t\n #$%&\'()*;<=>?[{|}~]|^$')
|
225
|
+
|
226
|
+
# _escape is a pattern that should match any character that needs to be
|
227
|
+
# escaped with a backslash, whether or not the argument matched the _quote
|
228
|
+
# pattern. _escape is used with re.sub to backslash anything in _escape's
|
229
|
+
# first match group, hence the (parentheses) in the regular expression.
|
230
|
+
#
|
231
|
+
# _escape matches the following characters appearing anywhere in an argument:
|
232
|
+
# " to prevent POSIX shells from interpreting this character for quoting
|
233
|
+
# \ to prevent POSIX shells from interpreting this character for escaping
|
234
|
+
# ` to prevent POSIX shells from interpreting this character for command
|
235
|
+
# substitution
|
236
|
+
# Missing from this list is $, because the desired behavior of
|
237
|
+
# EncodePOSIXShellArgument is to permit parameter (variable) expansion.
|
238
|
+
#
|
239
|
+
# Also missing from this list is !, which bash will interpret as the history
|
240
|
+
# expansion character when history is enabled. bash does not enable history
|
241
|
+
# by default in non-interactive shells, so this is not thought to be a problem.
|
242
|
+
# ! was omitted from this list because bash interprets "\!" as a literal string
|
243
|
+
# including the backslash character (avoiding history expansion but retaining
|
244
|
+
# the backslash), which would not be correct for argument encoding. Handling
|
245
|
+
# this case properly would also be problematic because bash allows the history
|
246
|
+
# character to be changed with the histchars shell variable. Fortunately,
|
247
|
+
# as history is not enabled in non-interactive shells and
|
248
|
+
# EncodePOSIXShellArgument is only expected to encode for non-interactive
|
249
|
+
# shells, there is no room for error here by ignoring !.
|
250
|
+
_escape = re.compile(r'(["\\`])')
|
251
|
+
|
252
|
+
def EncodePOSIXShellArgument(argument):
|
253
|
+
"""Encodes |argument| suitably for consumption by POSIX shells.
|
254
|
+
|
255
|
+
argument may be quoted and escaped as necessary to ensure that POSIX shells
|
256
|
+
treat the returned value as a literal representing the argument passed to
|
257
|
+
this function. Parameter (variable) expansions beginning with $ are allowed
|
258
|
+
to remain intact without escaping the $, to allow the argument to contain
|
259
|
+
references to variables to be expanded by the shell.
|
260
|
+
"""
|
261
|
+
|
262
|
+
if not isinstance(argument, str):
|
263
|
+
argument = str(argument)
|
264
|
+
|
265
|
+
if _quote.search(argument):
|
266
|
+
quote = '"'
|
267
|
+
else:
|
268
|
+
quote = ''
|
269
|
+
|
270
|
+
encoded = quote + re.sub(_escape, r'\\\1', argument) + quote
|
271
|
+
|
272
|
+
return encoded
|
273
|
+
|
274
|
+
|
275
|
+
def EncodePOSIXShellList(list):
|
276
|
+
"""Encodes |list| suitably for consumption by POSIX shells.
|
277
|
+
|
278
|
+
Returns EncodePOSIXShellArgument for each item in list, and joins them
|
279
|
+
together using the space character as an argument separator.
|
280
|
+
"""
|
281
|
+
|
282
|
+
encoded_arguments = []
|
283
|
+
for argument in list:
|
284
|
+
encoded_arguments.append(EncodePOSIXShellArgument(argument))
|
285
|
+
return ' '.join(encoded_arguments)
|
286
|
+
|
287
|
+
|
288
|
+
def DeepDependencyTargets(target_dicts, roots):
|
289
|
+
"""Returns the recursive list of target dependencies."""
|
290
|
+
dependencies = set()
|
291
|
+
pending = set(roots)
|
292
|
+
while pending:
|
293
|
+
# Pluck out one.
|
294
|
+
r = pending.pop()
|
295
|
+
# Skip if visited already.
|
296
|
+
if r in dependencies:
|
297
|
+
continue
|
298
|
+
# Add it.
|
299
|
+
dependencies.add(r)
|
300
|
+
# Add its children.
|
301
|
+
spec = target_dicts[r]
|
302
|
+
pending.update(set(spec.get('dependencies', [])))
|
303
|
+
pending.update(set(spec.get('dependencies_original', [])))
|
304
|
+
return list(dependencies - set(roots))
|
305
|
+
|
306
|
+
|
307
|
+
def BuildFileTargets(target_list, build_file):
|
308
|
+
"""From a target_list, returns the subset from the specified build_file.
|
309
|
+
"""
|
310
|
+
return [p for p in target_list if BuildFile(p) == build_file]
|
311
|
+
|
312
|
+
|
313
|
+
def AllTargets(target_list, target_dicts, build_file):
|
314
|
+
"""Returns all targets (direct and dependencies) for the specified build_file.
|
315
|
+
"""
|
316
|
+
bftargets = BuildFileTargets(target_list, build_file)
|
317
|
+
deptargets = DeepDependencyTargets(target_dicts, bftargets)
|
318
|
+
return bftargets + deptargets
|
319
|
+
|
320
|
+
|
321
|
+
def WriteOnDiff(filename):
|
322
|
+
"""Write to a file only if the new contents differ.
|
323
|
+
|
324
|
+
Arguments:
|
325
|
+
filename: name of the file to potentially write to.
|
326
|
+
Returns:
|
327
|
+
A file like object which will write to temporary file and only overwrite
|
328
|
+
the target if it differs (on close).
|
329
|
+
"""
|
330
|
+
|
331
|
+
class Writer:
|
332
|
+
"""Wrapper around file which only covers the target if it differs."""
|
333
|
+
def __init__(self):
|
334
|
+
# Pick temporary file.
|
335
|
+
tmp_fd, self.tmp_path = tempfile.mkstemp(
|
336
|
+
suffix='.tmp',
|
337
|
+
prefix=os.path.split(filename)[1] + '.gyp.',
|
338
|
+
dir=os.path.split(filename)[0])
|
339
|
+
try:
|
340
|
+
self.tmp_file = os.fdopen(tmp_fd, 'wb')
|
341
|
+
except Exception:
|
342
|
+
# Don't leave turds behind.
|
343
|
+
os.unlink(self.tmp_path)
|
344
|
+
raise
|
345
|
+
|
346
|
+
def __getattr__(self, attrname):
|
347
|
+
# Delegate everything else to self.tmp_file
|
348
|
+
return getattr(self.tmp_file, attrname)
|
349
|
+
|
350
|
+
def close(self):
|
351
|
+
try:
|
352
|
+
# Close tmp file.
|
353
|
+
self.tmp_file.close()
|
354
|
+
# Determine if different.
|
355
|
+
same = False
|
356
|
+
try:
|
357
|
+
same = filecmp.cmp(self.tmp_path, filename, False)
|
358
|
+
except OSError, e:
|
359
|
+
if e.errno != errno.ENOENT:
|
360
|
+
raise
|
361
|
+
|
362
|
+
if same:
|
363
|
+
# The new file is identical to the old one, just get rid of the new
|
364
|
+
# one.
|
365
|
+
os.unlink(self.tmp_path)
|
366
|
+
else:
|
367
|
+
# The new file is different from the old one, or there is no old one.
|
368
|
+
# Rename the new file to the permanent name.
|
369
|
+
#
|
370
|
+
# tempfile.mkstemp uses an overly restrictive mode, resulting in a
|
371
|
+
# file that can only be read by the owner, regardless of the umask.
|
372
|
+
# There's no reason to not respect the umask here, which means that
|
373
|
+
# an extra hoop is required to fetch it and reset the new file's mode.
|
374
|
+
#
|
375
|
+
# No way to get the umask without setting a new one? Set a safe one
|
376
|
+
# and then set it back to the old value.
|
377
|
+
umask = os.umask(077)
|
378
|
+
os.umask(umask)
|
379
|
+
os.chmod(self.tmp_path, 0666 & ~umask)
|
380
|
+
if sys.platform == 'win32' and os.path.exists(filename):
|
381
|
+
# NOTE: on windows (but not cygwin) rename will not replace an
|
382
|
+
# existing file, so it must be preceded with a remove. Sadly there
|
383
|
+
# is no way to make the switch atomic.
|
384
|
+
os.remove(filename)
|
385
|
+
os.rename(self.tmp_path, filename)
|
386
|
+
except Exception:
|
387
|
+
# Don't leave turds behind.
|
388
|
+
os.unlink(self.tmp_path)
|
389
|
+
raise
|
390
|
+
|
391
|
+
return Writer()
|
392
|
+
|
393
|
+
|
394
|
+
def EnsureDirExists(path):
|
395
|
+
"""Make sure the directory for |path| exists."""
|
396
|
+
try:
|
397
|
+
os.makedirs(os.path.dirname(path))
|
398
|
+
except OSError:
|
399
|
+
pass
|
400
|
+
|
401
|
+
|
402
|
+
def GetFlavor(params):
|
403
|
+
"""Returns |params.flavor| if it's set, the system's default flavor else."""
|
404
|
+
flavors = {
|
405
|
+
'cygwin': 'win',
|
406
|
+
'win32': 'win',
|
407
|
+
'darwin': 'mac',
|
408
|
+
}
|
409
|
+
|
410
|
+
if 'flavor' in params:
|
411
|
+
return params['flavor']
|
412
|
+
if sys.platform in flavors:
|
413
|
+
return flavors[sys.platform]
|
414
|
+
if sys.platform.startswith('sunos'):
|
415
|
+
return 'solaris'
|
416
|
+
if sys.platform.startswith('freebsd'):
|
417
|
+
return 'freebsd'
|
418
|
+
if sys.platform.startswith('openbsd'):
|
419
|
+
return 'openbsd'
|
420
|
+
if sys.platform.startswith('aix'):
|
421
|
+
return 'aix'
|
422
|
+
|
423
|
+
return 'linux'
|
424
|
+
|
425
|
+
|
426
|
+
def CopyTool(flavor, out_path):
|
427
|
+
"""Finds (flock|mac|win)_tool.gyp in the gyp directory and copies it
|
428
|
+
to |out_path|."""
|
429
|
+
# aix and solaris just need flock emulation. mac and win use more complicated
|
430
|
+
# support scripts.
|
431
|
+
prefix = {
|
432
|
+
'aix': 'flock',
|
433
|
+
'solaris': 'flock',
|
434
|
+
'mac': 'mac',
|
435
|
+
'win': 'win'
|
436
|
+
}.get(flavor, None)
|
437
|
+
if not prefix:
|
438
|
+
return
|
439
|
+
|
440
|
+
# Slurp input file.
|
441
|
+
source_path = os.path.join(
|
442
|
+
os.path.dirname(os.path.abspath(__file__)), '%s_tool.py' % prefix)
|
443
|
+
with open(source_path) as source_file:
|
444
|
+
source = source_file.readlines()
|
445
|
+
|
446
|
+
# Add header and write it out.
|
447
|
+
tool_path = os.path.join(out_path, 'gyp-%s-tool' % prefix)
|
448
|
+
with open(tool_path, 'w') as tool_file:
|
449
|
+
tool_file.write(
|
450
|
+
''.join([source[0], '# Generated by gyp. Do not edit.\n'] + source[1:]))
|
451
|
+
|
452
|
+
# Make file executable.
|
453
|
+
os.chmod(tool_path, 0755)
|
454
|
+
|
455
|
+
|
456
|
+
# From Alex Martelli,
|
457
|
+
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
|
458
|
+
# ASPN: Python Cookbook: Remove duplicates from a sequence
|
459
|
+
# First comment, dated 2001/10/13.
|
460
|
+
# (Also in the printed Python Cookbook.)
|
461
|
+
|
462
|
+
def uniquer(seq, idfun=None):
|
463
|
+
if idfun is None:
|
464
|
+
idfun = lambda x: x
|
465
|
+
seen = {}
|
466
|
+
result = []
|
467
|
+
for item in seq:
|
468
|
+
marker = idfun(item)
|
469
|
+
if marker in seen: continue
|
470
|
+
seen[marker] = 1
|
471
|
+
result.append(item)
|
472
|
+
return result
|
473
|
+
|
474
|
+
|
475
|
+
class CycleError(Exception):
|
476
|
+
"""An exception raised when an unexpected cycle is detected."""
|
477
|
+
def __init__(self, nodes):
|
478
|
+
self.nodes = nodes
|
479
|
+
def __str__(self):
|
480
|
+
return 'CycleError: cycle involving: ' + str(self.nodes)
|
481
|
+
|
482
|
+
|
483
|
+
def TopologicallySorted(graph, get_edges):
|
484
|
+
"""Topologically sort based on a user provided edge definition.
|
485
|
+
|
486
|
+
Args:
|
487
|
+
graph: A list of node names.
|
488
|
+
get_edges: A function mapping from node name to a hashable collection
|
489
|
+
of node names which this node has outgoing edges to.
|
490
|
+
Returns:
|
491
|
+
A list containing all of the node in graph in topological order.
|
492
|
+
It is assumed that calling get_edges once for each node and caching is
|
493
|
+
cheaper than repeatedly calling get_edges.
|
494
|
+
Raises:
|
495
|
+
CycleError in the event of a cycle.
|
496
|
+
Example:
|
497
|
+
graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'}
|
498
|
+
def GetEdges(node):
|
499
|
+
return re.findall(r'\$\(([^))]\)', graph[node])
|
500
|
+
print TopologicallySorted(graph.keys(), GetEdges)
|
501
|
+
==>
|
502
|
+
['a', 'c', b']
|
503
|
+
"""
|
504
|
+
get_edges = memoize(get_edges)
|
505
|
+
visited = set()
|
506
|
+
visiting = set()
|
507
|
+
ordered_nodes = []
|
508
|
+
def Visit(node):
|
509
|
+
if node in visiting:
|
510
|
+
raise CycleError(visiting)
|
511
|
+
if node in visited:
|
512
|
+
return
|
513
|
+
visited.add(node)
|
514
|
+
visiting.add(node)
|
515
|
+
for neighbor in get_edges(node):
|
516
|
+
Visit(neighbor)
|
517
|
+
visiting.remove(node)
|
518
|
+
ordered_nodes.insert(0, node)
|
519
|
+
for node in sorted(graph):
|
520
|
+
Visit(node)
|
521
|
+
return ordered_nodes
|