3 # Copyright 2014 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 from __future__ import print_function
10 import multiprocessing
20 SCRIPTS_DIR = os.path.abspath(os.path.dirname(__file__))
21 FFMPEG_DIR = os.path.abspath(os.path.join(SCRIPTS_DIR, '..', '..'))
32 USAGE = """Usage: %prog TARGET_OS TARGET_ARCH [options] -- [configure_args]
34 Valid combinations are linux [ia32|x64|mipsel|arm|arm-neon]
39 Platform specific build notes:
41 Script can run on a normal Ubuntu box.
44 Script can run on a normal Ubuntu box with MIPS cross-toolchain in $PATH.
47 Script must be run inside of ChromeOS chroot with BOARD=arm-generic.
50 Script must be run on OSX. Additionally, ensure the Chromium (not Apple)
51 version of clang is in the path; usually found under
52 src/third_party/llvm-build/Release+Asserts/bin
55 Script must be run on Windows with VS2013 or higher under Cygwin (or MinGW,
56 but as of 1.0.11, it has serious performance issues with make which makes
59 Additionall, ensure you have the correct toolchain environment for building.
60 The x86 toolchain environment is required for ia32 builds and the x64 one
61 for x64 builds. This can be verified by running "cl.exe" and checking if
62 the version string ends with "for x64" or "for x86."
64 Building on Windows also requires some additional Cygwin packages plus a
65 wrapper script for converting Cygwin paths to DOS paths.
66 - Add these packages at install time: diffutils, yasm, make, python.
67 - Copy chromium/scripts/cygwin-wrapper to /usr/local/bin
69 Resulting binaries will be placed in:
70 build.TARGET_ARCH.TARGET_OS/Chromium/out/
71 build.TARGET_ARCH.TARGET_OS/Chrome/out/
72 build.TARGET_ARCH.TARGET_OS/ChromiumOS/out/
73 build.TARGET_ARCH.TARGET_OS/ChromeOS/out/"""
76 def PrintAndCheckCall(argv, *args, **kwargs):
77 print('Running %r' % argv)
78 subprocess.check_call(argv, *args, **kwargs)
81 def DetermineHostOsAndArch():
82 if platform.system() == 'Linux':
84 elif platform.system() == 'Darwin':
86 elif platform.system() == 'Windows' or platform.system() == 'CYGWIN_NT-6.1':
91 if re.match(r'i.86', platform.machine()):
93 elif platform.machine() == 'x86_64' or platform.machine() == 'AMD64':
95 elif platform.machine().startswith('arm'):
100 return (host_os, host_arch)
103 def GetDsoName(target_os, dso_name, dso_version):
104 if target_os == 'linux':
105 return 'lib%s.so.%s' % (dso_name, dso_version)
106 elif target_os == 'mac':
107 return 'lib%s.%s.dylib' % (dso_name, dso_version)
108 elif target_os == 'win':
109 return '%s-%s.dll' % (dso_name, dso_version)
111 raise ValueError('Unexpected target_os %s' % target_os)
114 def RewriteFile(path, search, replace):
115 with open(path) as f:
117 with open(path, 'w') as f:
118 f.write(re.sub(search, replace, contents))
121 def BuildFFmpeg(target_os, target_arch, host_os, host_arch, parallel_jobs,
122 config_only, config, configure_flags):
123 config_dir = 'build.%s.%s/%s' % (target_arch, target_os, config)
124 shutil.rmtree(config_dir, ignore_errors=True)
125 os.makedirs(os.path.join(config_dir, 'out'))
128 [os.path.join(FFMPEG_DIR, 'configure')] + configure_flags, cwd=config_dir)
130 if (target_os, target_arch) == ('mac', 'ia32'):
131 # Required to get Mac ia32 builds compiling with -fno-omit-frame-pointer,
132 # which is required for accurate stack traces. See http://crbug.com/115170.
134 # Without this, building without -fomit-frame-pointer on ia32 will result in
135 # the the inclusion of a number of inline assembly blocks that use too many
136 # registers for its input/output operands.
137 for name in ('config.h', 'config.asm'):
138 RewriteFile(os.path.join(config_dir, name),
139 'HAVE_EBP_AVAILABLE 1',
140 'HAVE_EBP_AVAILABLE 0')
142 if host_os == target_os and not config_only:
144 os.path.join('libavcodec', GetDsoName(target_os, 'avcodec', 55)),
145 os.path.join('libavformat', GetDsoName(target_os, 'avformat', 55)),
146 os.path.join('libavutil', GetDsoName(target_os, 'avutil', 52)),
149 ['make', '-j%d' % parallel_jobs] + libraries, cwd=config_dir)
150 for lib in libraries:
151 shutil.copy(os.path.join(config_dir, lib),
152 os.path.join(config_dir, 'out'))
154 print('Skipping build step as requested.')
156 print('Skipping compile as host configuration differs from target.\n'
157 'Please compare the generated config.h with the previous version.\n'
158 'You may also patch the script to properly cross-compile.\n'
162 'Target arch : %s\n' % (host_os, target_os, host_arch, target_arch))
164 if target_arch in ('arm', 'arm-neon'):
166 os.path.join(config_dir, 'config.h'),
167 r'(#define HAVE_VFP_ARGS [01])',
168 r'/* \1 -- Disabled to allow softfp/hardfp selection at gyp time */')
172 parser = optparse.OptionParser(usage=USAGE)
173 parser.add_option('--branding', action='append', dest='brandings',
175 help='Branding to build; determines e.g. supported codecs')
176 parser.add_option('--config-only', action='store_true',
177 help='Skip the build step. Useful when a given platform '
178 'is not necessary for generate_gyp.py')
179 options, args = parser.parse_args(argv)
186 target_arch = args[1]
187 configure_args = args[2:]
189 if target_os not in ('linux', 'linux-noasm', 'win', 'mac'):
193 host_tuple = DetermineHostOsAndArch()
195 print('Unrecognized host OS and architecture.', file=sys.stderr)
198 host_os, host_arch = host_tuple
199 parallel_jobs = multiprocessing.cpu_count()
201 print('System information:\n'
206 'Parallel jobs : %d\n' % (
207 host_os, target_os, host_arch, target_arch, parallel_jobs))
209 configure_flags = collections.defaultdict(list)
211 # Common configuration. Note: --disable-everything does not in fact disable
212 # everything, just non-library components such as decoders and demuxers.
213 configure_flags['Common'].extend([
214 '--disable-everything',
227 '--disable-error-resilience',
235 # Disable hardware decoding options which will sometimes turn on
243 '--enable-decoder=theora,vorbis,vp8',
244 '--enable-decoder=pcm_u8,pcm_s16le,pcm_s24le,pcm_f32le',
245 '--enable-decoder=pcm_s16be,pcm_s24be,pcm_mulaw,pcm_alaw',
246 '--enable-demuxer=ogg,matroska,wav',
247 '--enable-parser=opus,vp3,vorbis,vp8',
250 # --optflags doesn't append multiple entries, so set all at once.
251 if (target_os, target_arch) == ('mac', 'ia32'):
252 configure_flags['Common'].append('--optflags="-fno-omit-frame-pointer -O2"')
254 configure_flags['Common'].append('--optflags="-O2"')
257 if target_os in ('linux', 'linux-noasm'):
258 if target_arch == 'x64':
260 elif target_arch == 'ia32':
261 configure_flags['Common'].extend([
264 '--extra-cflags="-m32"',
265 '--extra-ldflags="-m32"',
267 elif target_arch == 'arm':
268 if host_arch != 'arm':
269 configure_flags['Common'].extend([
270 # This if-statement essentially is for chroot tegra2.
271 '--enable-cross-compile',
273 # Location is for CrOS chroot. If you want to use this, enter chroot
274 # and copy ffmpeg to a location that is reachable.
275 '--cross-prefix=/usr/bin/armv7a-cros-linux-gnueabi-',
280 # TODO(ihf): ARM compile flags are tricky. The final options
281 # overriding everything live in chroot /build/*/etc/make.conf
282 # (some of them coming from src/overlays/overlay-<BOARD>/make.conf).
283 # We try to follow these here closely. In particular we need to
284 # set ffmpeg internal #defines to conform to make.conf.
285 # TODO(ihf): For now it is not clear if thumb or arm settings would be
286 # faster. I ran experiments in other contexts and performance seemed
287 # to be close and compiler version dependent. In practice thumb builds are
288 # much smaller than optimized arm builds, hence we go with the global
290 configure_flags['Common'].extend([
296 '--extra-cflags=-march=armv7-a',
297 '--extra-cflags=-mtune=cortex-a8',
298 '--extra-cflags=-mfpu=vfpv3-d16',
299 # NOTE: softfp/hardfp selected at gyp time.
300 '--extra-cflags=-mfloat-abi=hard',
302 elif target_arch == 'arm-neon':
303 if host_arch != 'arm':
304 # This if-statement is for chroot arm-generic.
305 configure_flags['Common'].extend([
306 '--enable-cross-compile',
307 '--cross-prefix=/usr/bin/armv7a-cros-linux-gnueabi-',
311 configure_flags['Common'].extend([
317 '--extra-cflags=-march=armv7-a',
318 '--extra-cflags=-mtune=cortex-a8',
319 '--extra-cflags=-mfpu=neon',
320 # NOTE: softfp/hardfp selected at gyp time.
321 '--extra-cflags=-mfloat-abi=hard',
323 elif target_arch == 'mipsel':
324 configure_flags['Common'].extend([
325 '--enable-cross-compile',
326 '--cross-prefix=mips-linux-gnu-',
329 '--extra-cflags=-mips32',
330 '--extra-cflags=-EL',
331 '--extra-ldflags=-mips32',
332 '--extra-ldflags=-EL',
334 '--disable-mipsdspr1',
335 '--disable-mipsdspr2',
338 print('Error: Unknown target arch %r for target OS %r!' % (
339 target_arch, target_os), file=sys.stderr)
342 if target_os == 'linux-noasm':
343 configure_flags['Common'].extend([
345 '--disable-inline-asm',
348 if 'win' not in target_os:
349 configure_flags['Common'].append('--enable-pic')
351 # Should be run on Mac.
352 if target_os == 'mac':
354 print('Script should be run on a Mac host. If this is not possible\n'
355 'try a merge of config files with new linux ia32 config.h\n'
356 'by hand.\n', file=sys.stderr)
359 configure_flags['Common'].extend([
364 if target_arch == 'ia32':
365 configure_flags['Common'].extend([
367 '--extra-cflags=-m32',
368 '--extra-ldflags=-m32',
370 elif target_arch == 'x64':
371 configure_flags['Common'].extend([
373 '--extra-cflags=-m64',
374 '--extra-ldflags=-m64',
377 print('Error: Unknown target arch %r for target OS %r!' % (
378 target_arch, target_os), file=sys.stderr)
380 # Should be run on Windows.
381 if target_os == 'win':
383 print('Script should be run on a Windows host.\n', file=sys.stderr)
386 configure_flags['Common'].extend([
389 '--extra-cflags=-I' + os.path.join(FFMPEG_DIR, 'chromium/include/win'),
392 if platform.system() == 'CYGWIN_NT-6.1':
393 configure_flags['Common'].extend([
394 '--cc=cygwin-wrapper cl',
395 '--ld=cygwin-wrapper link',
396 '--nm=cygwin-wrapper dumpbin -symbols',
397 '--ar=cygwin-wrapper lib',
400 # Google Chrome & ChromeOS specific configuration.
401 configure_flags['Chrome'].extend([
402 '--enable-decoder=aac,h264,mp3',
403 '--enable-demuxer=aac,mp3,mov',
404 '--enable-parser=aac,h264,mpegaudio',
407 # ChromiumOS specific configuration.
408 # Warning: do *NOT* add avi, aac, h264, mp3, mp4, amr*
410 configure_flags['ChromiumOS'].extend([
411 '--enable-demuxer=flac',
412 '--enable-decoder=flac',
413 '--enable-parser=flac',
416 # Google ChromeOS specific configuration.
417 # We want to make sure to play everything Android generates and plays.
418 # http://developer.android.com/guide/appendix/media-formats.html
419 configure_flags['ChromeOS'].extend([
420 # Enable playing avi files.
421 '--enable-decoder=mpeg4',
422 '--enable-parser=h263,mpeg4video',
423 '--enable-demuxer=avi',
424 # Enable playing Android 3gp files.
425 '--enable-demuxer=amr',
426 '--enable-decoder=amrnb,amrwb',
428 '--enable-demuxer=flac',
429 '--enable-decoder=flac',
430 '--enable-parser=flac',
431 # Wav files for playing phone messages.
432 '--enable-decoder=gsm_ms',
433 '--enable-demuxer=gsm',
434 '--enable-parser=gsm',
437 def do_build_ffmpeg(branding, configure_flags):
438 if options.brandings and branding not in options.brandings:
439 print('%s skipped' % branding)
442 print('%s configure/build:' % branding)
443 BuildFFmpeg(target_os, target_arch, host_os, host_arch, parallel_jobs,
444 options.config_only, branding, configure_flags)
446 do_build_ffmpeg('Chromium',
447 configure_flags['Common'] +
448 configure_flags['Chromium'] +
450 do_build_ffmpeg('Chrome',
451 configure_flags['Common'] +
452 configure_flags['Chrome'] +
455 if target_os == 'linux':
456 do_build_ffmpeg('ChromiumOS',
457 configure_flags['Common'] +
458 configure_flags['Chromium'] +
459 configure_flags['ChromiumOS'] +
462 # ChromeOS enables MPEG4 which requires error resilience :(
463 chrome_os_flags = (configure_flags['Common'] +
464 configure_flags['Chrome'] +
465 configure_flags['ChromeOS'] +
467 chrome_os_flags.remove('--disable-error-resilience')
468 do_build_ffmpeg('ChromeOS', chrome_os_flags)
470 print('Done. If desired you may copy config.h/config.asm into the '
471 'source/config tree using copy_config.sh.')
475 if __name__ == '__main__':
476 sys.exit(main(sys.argv[1:]))