3 """Writes large manifest files, for manifest parser performance testing.
5 The generated manifest files are (eerily) similar in appearance and size to the
6 ones used in the Chromium project.
9 python misc/write_fake_manifests.py outdir # Will run for about 5s.
11 The program contains a hardcoded random seed, so it will generate the same
12 output every time it runs. By changing the seed, it's easy to generate many
13 different sets of manifest files.
25 def paretoint(avg, alpha):
26 """Returns a random integer that's avg on average, following a power law.
27 alpha determines the shape of the power curve. alpha has to be larger
28 than 1. The closer alpha is to 1, the higher the variation of the returned
30 return int(random.paretovariate(alpha) * avg / (alpha / (alpha - 1)))
33 # Based on http://neugierig.org/software/chromium/class-name-generator.html
34 def moar(avg_options, p_suffix):
35 kStart = ['render', 'web', 'browser', 'tab', 'content', 'extension', 'url',
36 'file', 'sync', 'content', 'http', 'profile']
37 kOption = ['view', 'host', 'holder', 'container', 'impl', 'ref',
38 'delegate', 'widget', 'proxy', 'stub', 'context',
39 'manager', 'master', 'watcher', 'service', 'file', 'data',
40 'resource', 'device', 'info', 'provider', 'internals', 'tracker',
42 kOS = ['win', 'mac', 'aura', 'linux', 'android', 'unittest', 'browsertest']
43 num_options = min(paretoint(avg_options, alpha=4), 5)
44 # The original allows kOption to repeat as long as no consecutive options
45 # repeat. This version doesn't allow any option repetition.
46 name = [random.choice(kStart)] + random.sample(kOption, num_options)
47 if random.random() < p_suffix:
48 name.append(random.choice(kOS))
52 class GenRandom(object):
54 self.seen_names = set([None])
55 self.seen_defines = set([None])
57 def _unique_string(self, seen, avg_options=1.3, p_suffix=0.1):
60 s = moar(avg_options, p_suffix)
64 def _n_unique_strings(self, n):
66 return [self._unique_string(seen, avg_options=3, p_suffix=0.4)
69 def target_name(self):
70 return self._unique_string(p_suffix=0, seen=self.seen_names)
73 return os.path.sep.join([
74 self._unique_string(self.seen_names, avg_options=1, p_suffix=0)
75 for _ in xrange(1 + paretoint(0.6, alpha=4))])
77 def src_obj_pairs(self, path, name):
78 num_sources = paretoint(55, alpha=2) + 1
79 return [(os.path.join('..', '..', path, s + '.cc'),
80 os.path.join('obj', path, '%s.%s.o' % (name, s)))
81 for s in self._n_unique_strings(num_sources)]
85 '-DENABLE_' + self._unique_string(self.seen_defines).upper()
86 for _ in xrange(paretoint(20, alpha=3))]
91 def __init__(self, gen, kind):
92 self.name = gen.target_name()
93 self.dir_path = gen.path()
94 self.ninja_file_path = os.path.join(
95 'obj', self.dir_path, self.name + '.ninja')
96 self.src_obj_pairs = gen.src_obj_pairs(self.dir_path, self.name)
98 self.output = os.path.join('lib' + self.name + '.a')
100 self.output = os.path.join(self.name)
101 self.defines = gen.defines()
104 self.has_compile_depends = random.random() < 0.4
108 return ['-I' + dep.dir_path for dep in self.deps]
111 def write_target_ninja(ninja, target):
112 compile_depends = None
113 if target.has_compile_depends:
114 compile_depends = os.path.join(
115 'obj', target.dir_path, target.name + '.stamp')
116 ninja.build(compile_depends, 'stamp', target.src_obj_pairs[0][0])
119 ninja.variable('defines', target.defines)
121 ninja.variable('includes', target.includes)
122 ninja.variable('cflags', ['-Wall', '-fno-rtti', '-fno-exceptions'])
125 for src, obj in target.src_obj_pairs:
126 ninja.build(obj, 'cxx', src, implicit=compile_depends)
129 deps = [dep.output for dep in target.deps]
130 libs = [dep.output for dep in target.deps if dep.kind == LIB]
131 if target.kind == EXE:
132 ninja.variable('ldflags', '-Wl,pie')
133 ninja.variable('libs', libs)
134 link = { LIB: 'alink', EXE: 'link'}[target.kind]
135 ninja.build(target.output, link, [obj for _, obj in target.src_obj_pairs],
139 def write_master_ninja(master_ninja, targets):
140 """Writes master build.ninja file, referencing all given subninjas."""
141 master_ninja.variable('cxx', 'c++')
142 master_ninja.variable('ld', '$cxx')
143 master_ninja.newline()
145 master_ninja.pool('link_pool', depth=4)
146 master_ninja.newline()
148 master_ninja.rule('cxx', description='CXX $out',
149 command='$cxx -MMD -MF $out.d $defines $includes $cflags -c $in -o $out',
150 depfile='$out.d', deps='gcc')
151 master_ninja.rule('alink', description='LIBTOOL-STATIC $out',
152 command='rm -f $out && libtool -static -o $out $in')
153 master_ninja.rule('link', description='LINK $out', pool='link_pool',
154 command='$ld $ldflags -o $out $in $libs')
155 master_ninja.rule('stamp', description='STAMP $out', command='touch $out')
156 master_ninja.newline()
158 for target in targets:
159 master_ninja.subninja(target.ninja_file_path)
160 master_ninja.newline()
162 master_ninja.comment('Short names for targets.')
163 for target in targets:
164 if target.name != target.output:
165 master_ninja.build(target.name, 'phony', target.output)
166 master_ninja.newline()
168 master_ninja.build('all', 'phony', [target.output for target in targets])
169 master_ninja.default('all')
172 @contextlib.contextmanager
173 def FileWriter(path):
174 """Context manager for a ninja_syntax object writing to a file."""
176 os.makedirs(os.path.dirname(path))
180 yield ninja_syntax.Writer(f)
184 def random_targets():
188 # N-1 static libraries, and 1 executable depending on all of them.
189 targets = [Target(gen, LIB) for i in xrange(num_targets - 1)]
190 for i in range(len(targets)):
191 targets[i].deps = [t for t in targets[0:i] if random.random() < 0.05]
193 last_target = Target(gen, EXE)
194 last_target.deps = targets[:]
195 last_target.src_obj_pairs = last_target.src_obj_pairs[0:10] # Trim.
196 targets.append(last_target)
201 parser = argparse.ArgumentParser()
202 parser.add_argument('outdir', help='output directory')
203 args = parser.parse_args()
204 root_dir = args.outdir
208 targets = random_targets()
209 for target in targets:
210 with FileWriter(os.path.join(root_dir, target.ninja_file_path)) as n:
211 write_target_ninja(n, target)
213 with FileWriter(os.path.join(root_dir, 'build.ninja')) as master_ninja:
214 master_ninja.width = 120
215 write_master_ninja(master_ninja, targets)
218 if __name__ == '__main__':