[WebRTC] Add libwebrtc build infrastructure
[WebKit-https.git] / Tools / Scripts / webkitpy / libwebrtc / generate_cmake.py
1 import json
2 import logging
3 import re
4
5 from webkitpy.common.host import Host
6 from webkitpy.common.system.filesystem import FileSystem
7
8 CHANGESET_NOT_AVAILABLE = 'Not Available'
9
10 _log = logging.getLogger(__name__)
11
12
13 # Notes:
14 # Generation of info.plist for the framework is not triggered, using a static version instead in Source/ThirdParty/libwebrtc/WebKit.
15 # Generation expects that ".." refers to the root of the libwebrtc repository folder
16 # - input json file should be generated from gn using the --ide=json argument on a direct child folder of the libwebrtc repository folder
17 # cd /Users/Alice/WebKit/Source/ThirdParty/libwebrtc/libwebrtc
18 # gn gen out '--args=is_component_build=false rtc_include_tests=true rtc_use_h264=true rtc_libvpx_build_vp9=false rtc_libvpx_build_vp8=false target_os="mac" target_cpu="x64"' --ide=json
19
20 # The CMakeLists.txt uses  two variables:
21 # - LIBWEBRTC_INPUT_DIR: absolute path to the root of the libwebrtc folder (default to ${CMAKE_SOURCE_DIR}/libwebrtc)
22 # - LIBWEBRTC_OUTPUT_DIR: absolute path to the root of the build folder (default to ${CMAKE_BINARY_DIR})
23 # cd /Users/Alice/WebKit/Source/ThirdParty/libwebrtc/libwebrtc
24 # Tools/Scripts/generate-libwebrtc-cmake
25
26 # Before calling make, apply Source/ThirdParty/libwebrtc/WebKit/patch_libwebrtc_headers patch
27
28 # cmake /Users/Alice/WebKit/Source/ThirdParty/libwebrtc -B/Users/Alice/WebKit/WebKitBuild/Debug/libwebrtc
29 # cd /Users/Alice/WebKit/Source/ThirdParty/libwebrtc/libwebrtc
30 # git apply ../WebKit/patch_libwebrtc_headers
31 # make -C /Users/Alice/WebKit/WebKitBuild/Debug/libwebrtc
32
33
34 def main(args, _stdout, _stderr):
35     json_file = args[0] if len(args) >= 1 else "Source/ThirdParty/libwebrtc/WebKit/project.json"
36     output_file = args[1] if len(args) >= 2 else "Source/ThirdParty/libwebrtc/CMakeLists.txt"
37
38     configure_logging()
39
40     generator = CMakeGenerator(json_file, output_file)
41     generator.generate()
42
43
44 def configure_logging():
45     class LogHandler(logging.StreamHandler):
46
47         def format(self, record):
48             if record.levelno > logging.INFO:
49                 return "%s: %s" % (record.levelname, record.getMessage())
50             return record.getMessage()
51
52     logger = logging.getLogger()
53     logger.setLevel(logging.INFO)
54     handler = LogHandler()
55     handler.setLevel(logging.INFO)
56     logger.addHandler(handler)
57     return handler
58
59
60 class CMakeGenerator(object):
61
62     def __init__(self, inputFilename, outputFilename):
63         self.host = Host()
64         self.filesystem = FileSystem()
65         self.project = json.loads(self.filesystem.read_text_file(inputFilename))
66
67         self.enable_g711 = False
68         self.enable_g722 = False
69         # Current Openssl cannot really compile since they use deprecated openssl functions
70         self.enable_boringssl = True
71         self.enable_vpx = False
72         self.enable_libjpeg = False
73
74         self.targets = self.project["targets"]
75         self.outputFilename = outputFilename
76
77         self.skip_test_targets = True
78
79         self.starting_lines = ["cmake_minimum_required(VERSION 3.5)",
80                               "set(CMAKE_CXX_STANDARD 11)",
81                               "enable_language(ASM)",
82                               "",
83                               "if (NOT LIBWEBRTC_INPUT_DIR)",
84                               "    set(LIBWEBRTC_INPUT_DIR ${CMAKE_SOURCE_DIR}/Source)",
85                               "endif ()",
86                               "if (NOT LIBWEBRTC_OUTPUT_DIR)",
87                               "    set(LIBWEBRTC_OUTPUT_DIR ${CMAKE_BINARY_DIR})",
88                               "endif ()",
89                               "",
90                               "file(WRITE ${LIBWEBRTC_OUTPUT_DIR}/dummy.c \"\")",
91                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/obj/third_party/libjpeg_turbo/simd_asm)",
92                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/obj/third_party/ffmpeg/ffmpeg_yasm)",
93                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/obj/webrtc/sdk)",
94                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/third_party/yasm/include)",
95                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/webrtc/audio_coding/neteq)",
96                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/webrtc/logging/rtc_event_log)",
97                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/webrtc/modules/audio_coding/audio_network_adaptor)",
98                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/webrtc/modules/audio_processing)",
99                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/webrtc/sdk)",
100                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/gen/webrtc/tools/event_log_visualizer)",
101                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/pyproto/webrtc/audio_coding/neteq)",
102                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/pyproto/webrtc/logging/rtc_event_log)",
103                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/pyproto/webrtc/modules/audio_coding/audio_network_adaptor)",
104                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/pyproto/webrtc/modules/audio_coding/audio_network_adaptor)",
105                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/pyproto/webrtc/modules/audio_processing)",
106                               "file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/pyproto/webrtc/tools/event_log_visualizer)",
107                               "",
108                               ]
109
110         self.ending_lines = ["",
111                             "set_target_properties(WebrtcBaseGtest_Prod PROPERTIES LINKER_LANGUAGE CXX)",
112                             "set_target_properties(WebrtcLoggingRtc_Event_Log_Api PROPERTIES LINKER_LANGUAGE CXX)",
113                             ]
114         if self.enable_libjpeg:
115             self.ending_lines.append("set_target_properties(Third_PartyLibjpeg_TurboSimd_Asm PROPERTIES LINKER_LANGUAGE CXX)")
116
117         self.initialize_targets()
118
119     def initialize_targets(self):
120         # Simplifying generation
121         self.targets["//webrtc/sdk:rtc_sdk_framework_objc"]["sources"][:] = []
122         # Static_library requires as least one source file
123         self.targets["//webrtc/sdk:rtc_sdk_objc"]["sources"] = ["//out/dummy.c"]
124         # Executable target without any source file
125         self.targets["//webrtc:webrtc_tests"]["type"] = "group"
126         # Duplicate symbol issue with source_set
127         self.targets["//webrtc/api:call_api"]["type"] = "static_library"
128         # Simpler for linking WebCore
129         self.targets["//third_party/boringssl:boringssl"]["type"] = "static_library"
130         self.targets["//third_party/boringssl:boringssl"]["outputs"] = ["//out/libboringssl.a"]
131         # We use a static info plist instead of a dynamic one
132         del self.targets["//webrtc/sdk:rtc_sdk_framework_objc_info_plist"]
133         self.targets["//webrtc/sdk:rtc_sdk_framework_objc_info_plist_bundle_data"]["deps"].remove("//webrtc/sdk:rtc_sdk_framework_objc_info_plist")
134
135         # Macro to change specific things in LibWebRTC, only used in libjingle_peerconnection currently
136         self.targets["//webrtc/api:libjingle_peerconnection"]["defines"].append("WEBRTC_WEBKIT_BUILD")
137
138         if not self.enable_g711:
139             self.remove_webrtc_g711()
140         if not self.enable_g722:
141             self.remove_g722()
142
143         if self.enable_boringssl:
144             self.ending_lines.append("set_target_properties(Third_PartyBoringsslBoringssl_Asm PROPERTIES LINKER_LANGUAGE CXX)")
145         else:
146             self.remove_boringssl()
147
148         if self.enable_vpx:
149             self.ending_lines.append("set_target_properties(Third_PartyLibvpxLibvpx_Yasm PROPERTIES LINKER_LANGUAGE CXX)")
150             self.starting_lines.append("file(MAKE_DIRECTORY ${LIBWEBRTC_OUTPUT_DIR}/obj/third_party/libvpx/libvpx_yasm)")
151         else:
152             self.remove_libvpx()
153
154         self.remove_openmax_dl()
155
156         if not self.enable_libjpeg:
157             self.remove_libjpeg()
158             self.remove_yasm()
159
160         self.remove_webrtc_base_sha1()
161         self.targets.pop("//build/config/sanitizers:options_sources")
162
163     def _remove_target(self, targetName):
164         self.targets.pop(targetName)
165         for name, target in self.targets.iteritems():
166             if "deps" in target:
167                 deps = target["deps"]
168                 if targetName in deps:
169                     deps.remove(targetName)
170
171     def remove_webrtc_g711(self):
172         self._remove_target("//webrtc/modules/audio_coding:g711_test")
173         self._remove_target("//webrtc/modules/audio_coding:neteq_pcmu_quality_test")
174         self._remove_target("//webrtc/modules/audio_coding:audio_decoder_unittests")
175
176         self._remove_target("//webrtc/modules/audio_coding:g711")
177         for name, target in self.targets.iteritems():
178             if "include_dirs" in target:
179                 include_dirs = target["include_dirs"]
180                 if "//webrtc/modules/audio_coding/codecs/g711/include/" in include_dirs:
181                     include_dirs.remove("//webrtc/modules/audio_coding/codecs/g711/include/")
182
183             if "defines" in target:
184                 defines = target["defines"]
185                 if "CODEC_G711" in defines:
186                     defines.remove("CODEC_G711")
187
188     def remove_libjpeg(self):
189         self.targets.pop("//third_party/libjpeg_turbo:libjpeg")
190         self.targets.pop("//third_party:jpeg")
191         self.targets.pop("//third_party/libjpeg_turbo:simd")
192         self.targets.pop("//third_party/libjpeg_turbo:simd_asm")
193         self.targets.pop("//third_party/libjpeg_turbo:simd_asm_action")
194
195         libyuv = self.targets["//third_party/libyuv:libyuv"]
196         libyuv["deps"].remove("//third_party:jpeg")
197         libyuv["defines"].remove("HAVE_JPEG")
198         libyuv["defines"].remove("USE_LIBJPEG_TURBO=1")
199
200         self.targets["//third_party/libyuv:libyuv_unittest"]["defines"].remove("HAVE_JPEG")
201         self.targets["//third_party/libyuv:psnr"]["defines"].remove("HAVE_JPEG")
202
203         for name, target in self.targets.iteritems():
204             if "include_dirs" in target:
205                 include_dirs = target["include_dirs"]
206                 if "//third_party/openmax_dl/" in include_dirs:
207                     include_dirs.remove("//third_party/openmax_dl/")
208
209             if "deps" in target:
210                 deps = target["deps"]
211                 if "//third_party:jpeg" in deps:
212                     deps.remove("//third_party:jpeg")
213
214             if "defines" in target:
215                 defines = target["defines"]
216                 if "RTC_USE_OPENMAX_DL" in defines:
217                     defines.remove("RTC_USE_OPENMAX_DL")
218
219     def remove_webrtc_base_sha1(self):
220         base = self.targets["//webrtc/base:rtc_base"]
221         base["source_outputs"].pop("//webrtc/base/sha1.cc")
222         base["sources"].remove("//webrtc/base/sha1.cc")
223
224     def remove_yasm(self):
225         self.targets.pop("//third_party/yasm:yasm")
226         self.targets.pop("//third_party/yasm:compile_gperf")
227         self.targets.pop("//third_party/yasm:compile_gperf_for_include")
228         self.targets.pop("//third_party/yasm:compile_nasm_macros")
229         self.targets.pop("//third_party/yasm:compile_nasm_version")
230         self.targets.pop("//third_party/yasm:compile_re2c")
231         self.targets.pop("//third_party/yasm:compile_re2c_lc3b")
232         self.targets.pop("//third_party/yasm:compile_win64_gas")
233         self.targets.pop("//third_party/yasm:compile_win64_nasm")
234         self.targets.pop("//third_party/yasm:generate_license")
235         self.targets.pop("//third_party/yasm:generate_module")
236         self.targets.pop("//third_party/yasm:generate_version")
237         self.targets.pop("//third_party/yasm:yasm_utils")
238         self.targets.pop("//third_party/yasm:genperf")
239         self.targets.pop("//third_party/yasm:genmodule")
240         self.targets.pop("//third_party/yasm:re2c")
241         self.targets.pop("//third_party/yasm:genstring")
242         self.targets.pop("//third_party/yasm:genversion")
243         self.targets.pop("//third_party/yasm:genmacro")
244
245     def remove_openmax_dl(self):
246         self.targets.pop("//third_party/openmax_dl/dl:dl")
247         for name, target in self.targets.iteritems():
248             if "include_dirs" in target:
249                 include_dirs = target["include_dirs"]
250                 if "//third_party/openmax_dl/" in include_dirs:
251                     include_dirs.remove("//third_party/openmax_dl/")
252
253             if "deps" in target:
254                 deps = target["deps"]
255                 if "//third_party/openmax_dl/dl:dl" in deps:
256                     deps.remove("//third_party/openmax_dl/dl:dl")
257
258             if "defines" in target:
259                 defines = target["defines"]
260                 if "RTC_USE_OPENMAX_DL" in defines:
261                     defines.remove("RTC_USE_OPENMAX_DL")
262
263         common_audio = self.targets["//webrtc/common_audio:common_audio"]
264         common_audio["source_outputs"].pop("//webrtc/common_audio/real_fourier_openmax.cc")
265         common_audio["sources"].remove("//webrtc/common_audio/real_fourier_openmax.cc")
266
267     def remove_libvpx(self):
268         self.targets = {name: target for name, target in self.targets.iteritems() if not ("libvpx" in name or "vp9" in name or "vp8" in name)}
269         for name, target in self.targets.iteritems():
270             if "include_dirs" in target:
271                 include_dirs = target["include_dirs"]
272                 if "//third_party/libvpx/source/libvpx/" in include_dirs:
273                     include_dirs.remove("//third_party/libvpx/source/libvpx/")
274
275             if not "deps" in target:
276                 continue
277             target["deps"] = [dep for dep in target["deps"] if not ("libvpx" in dep or "vp9" in dep or "vp8" in dep)]
278
279         target = self.targets["//webrtc/modules/video_coding:video_coding"]
280         target["defines"].append("RTC_DISABLE_VP8")
281         target["defines"].append("RTC_DISABLE_VP9")
282         target["sources"].append("//webrtc/modules/video_coding/codecs/vp9/vp9_noop.cc")
283         target["source_outputs"]["//webrtc/modules/video_coding/codecs/vp9/vp9_noop.cc"] = ["obj/webrtc/modules/video_coding/webrtc_vp9/vp9_noop.o"]
284
285         target = self.targets["//webrtc/media:rtc_media"]
286         target["defines"].append("RTC_DISABLE_VP8")
287         target["defines"].append("RTC_DISABLE_VP9")
288
289     def remove_boringssl(self):
290         self.targets.pop("//third_party/boringssl:boringssl")
291         self.targets.pop("//third_party/boringssl:boringssl_asm")
292         for name, target in self.targets.iteritems():
293             if "include_dirs" in target:
294                 include_dirs = target["include_dirs"]
295                 if "//third_party/boringssl/src/include/" in include_dirs:
296                     include_dirs.remove("//third_party/boringssl/src/include/")
297                     #include_dirs.append("/usr/local/opt/openssl/include/")
298
299             if not "deps" in target:
300                 continue
301             deps = target["deps"]
302             if "//third_party/boringssl:boringssl" in deps:
303                 deps.remove("//third_party/boringssl:boringssl")
304                 # Do we need this one?
305                 target["defines"].append("OPENSSL_NO_SSL_INTERN")
306                 # Do we need to set -L for access to the libs?
307                 target["ldflags"].extend(["-lcrypto", "-lssl"])
308         self.targets["//webrtc/p2p:stun_prober"]["ldflags"].extend(["-lcrypto", "-lssl"])
309
310     def remove_g722(self):
311         self.targets.pop("//webrtc/modules/audio_coding:g722")
312         self.targets.pop("//webrtc/modules/audio_coding:g722_test")
313         for name, target in self.targets.iteritems():
314             if "defines" in target:
315                 defines = target["defines"]
316                 if "WEBRTC_CODEC_G722" in defines:
317                     defines.remove("WEBRTC_CODEC_G722")
318                 if "CODEC_G722" in defines:
319                     defines.remove("CODEC_G722")
320
321             if "include_dirs" in target:
322                 include_dirs = target["include_dirs"]
323                 if "//webrtc/modules/audio_coding/codecs/g722/include/" in include_dirs:
324                     include_dirs.remove("//webrtc/modules/audio_coding/codecs/g722/include/")
325
326             if not "deps" in target:
327                 continue
328             deps = target["deps"]
329             if "//webrtc/modules/audio_coding:g722" in deps:
330                 deps.remove("//webrtc/modules/audio_coding:g722")
331             if "//webrtc/modules/audio_coding:g722_test" in target["deps"]:
332                 deps.remove("//webrtc/modules/audio_coding:g722_test")
333
334     def generate(self):
335         lines = self.starting_lines
336
337         lines.extend(self._initialize_frameworks())
338
339         for name, target in self.targets.iteritems():
340             lines.append("\n".join(self.generate_target(self.sanitize_target_name(name), target)))
341
342         lines.extend(self.generate_libwebrtc_target())
343         lines.extend(self.ending_lines)
344         self.write_lazily("\n".join(lines))
345
346     def _initialize_frameworks(self):
347         lines = []
348         frameworks = []
349         for name, target in self.targets.iteritems():
350             if ('sdk' in name and not "peerconnection" in name):
351                 continue
352             if "libs" in target:
353                 frameworks.extend(target["libs"])
354         frameworks = list(set(frameworks))
355         for framework in frameworks:
356             framework = framework.replace(".framework", "")
357             lines.append("find_library(" + framework.upper() + "_LIBRARY " + framework + ")")
358
359         return lines
360
361     def write_lazily(self, content):
362         if (self.filesystem.exists(self.outputFilename)):
363             old_content = self.filesystem.read_text_file(self.outputFilename)
364             if old_content == content:
365                 return
366         self.filesystem.write_text_file(self.outputFilename, content)
367
368     def sanitize_target_name(self, name):
369         return "".join([step.title() for step in re.split('/|:', name)])
370
371     def convert_deps(self, names):
372         return " ".join([self.sanitize_target_name(name) for name in names])
373
374     def convert_source(self, source):
375         return source.replace("//out", "${LIBWEBRTC_OUTPUT_DIR}").replace("//", "${LIBWEBRTC_INPUT_DIR}/")
376
377     def convert_input(self, input):
378         return input.replace("//out", "${LIBWEBRTC_OUTPUT_DIR}").replace("//", "${LIBWEBRTC_INPUT_DIR}/")
379
380     def convert_inputs(self, inputs):
381         return " ".join(inputs).replace("//out", "${LIBWEBRTC_OUTPUT_DIR}").replace("//", "${LIBWEBRTC_INPUT_DIR}/")
382
383     def convert_output(self, output):
384         return output.replace("//out", "${LIBWEBRTC_OUTPUT_DIR}")
385
386     def convert_outputs(self, outputs):
387         return " ".join(outputs).replace("//out", "${LIBWEBRTC_OUTPUT_DIR}")
388
389     def generate_libwebrtc_target(self):
390         skipped_sources = [
391             "//webrtc/base/sha1.cc",
392             "//webrtc/base/sha1digest.cc",
393             "//webrtc/base/md5.cc",
394             "//webrtc/base/md5digest.cc",
395
396             "//webrtc/base/json.cc"
397             "//third_party/jsoncpp/overrides/src/lib_json/json_reader.cpp",
398             "//third_party/jsoncpp/overrides/src/lib_json/json_value.cpp",
399             "//third_party/jsoncpp/source/src/lib_json/json_writer.cpp"]
400         lines = []
401         lines.append("# Start of target LIBWEBRTC")
402         objects = []
403         dependencies = []
404         for name, target in self.targets.iteritems():
405             if target["testonly"] or name.startswith("//webrtc/examples"):
406                 continue
407             if "source_outputs" in target:
408                 for source, output in target["source_outputs"].iteritems():
409                     if source in skipped_sources:
410                         continue
411                     if source.endswith(".o"):
412                         continue
413                     dependencies.append(self.sanitize_target_name(name))
414                     if source.endswith(".asm"):
415                         objects.append(output[0].replace("_action", ""))
416                     elif output[0].endswith(".o"):
417                         filename = source.replace("//out/", "").replace("//", "Source/")
418                         if not filename.endswith(".o"):
419                             filename += ".o"
420                         objects.append(("CMakeFiles/" + self.sanitize_target_name(name) + ".dir/" + filename))
421         dependencies = list(set(dependencies))
422
423         lines.append("file(WRITE ${LIBWEBRTC_OUTPUT_DIR}/list_libwebrtc_objects \"" + "\n".join(objects) + "\")")
424         lines.append("add_custom_command(OUTPUT ${LIBWEBRTC_OUTPUT_DIR}/../libwebrtc.a")
425         lines.append("    COMMAND libtool -static -o ${LIBWEBRTC_OUTPUT_DIR}/../libwebrtc.a -filelist ${LIBWEBRTC_OUTPUT_DIR}/list_libwebrtc_objects")
426         lines.append("    VERBATIM)")
427
428         lines.append("add_custom_target(LIBWEBRTC DEPENDS " + " ".join(dependencies) + " ${LIBWEBRTC_OUTPUT_DIR}/../libwebrtc.a)")
429         lines.append("# End of target LIBWEBRTC")
430         return lines
431
432     def generate_target(self, name, target):
433         if (self.skip_test_targets and target["testonly"]) or name.startswith("WebrtcExamples"):
434             return []
435
436         lines = ["\n# Start of target " + name]
437         if target["type"] == "action":
438             lines.extend(self.generate_action_target(name, target))
439         elif target["type"] == "action_foreach":
440             lines.extend(self.generate_action_foreach_target(name, target))
441         elif target["type"] == "copy":
442             lines.extend(self.generate_copy_target(name, target))
443         elif target["type"] == "executable":
444             lines.extend(self.generate_executable_target(name, target))
445         elif target["type"] == "shared_library":
446             lines.extend(self.generate_shared_library_target(name, target))
447         elif target["type"] == "static_library":
448             lines.extend(self.generate_static_library_target(name, target))
449         elif target["type"] == "create_bundle":
450             lines.extend(self.generate_bundle_target(name, target))
451         elif target["type"] == "bundle_data":
452             lines.extend(self.generate_bundle_data_target(name, target))
453         elif target["type"] == "group":
454             lines.extend(self.generate_group_target(name, target))
455         elif target["type"] == "source_set":
456             lines.extend(self.generate_source_set_target(name, target))
457         else:
458             raise "unsupported target type: " + target["type"]
459         lines.append("# End of target " + name)
460         return lines
461
462     def convert_arguments(self, arguments):
463         value = ""
464         is_first = True
465         for argument in arguments:
466             if not is_first:
467                 value += " "
468             is_first = False
469
470             if (argument.startswith("../")):
471                 value += "${LIBWEBRTC_INPUT_DIR}/" + argument[3:]
472             elif (argument.startswith("gen/")):
473                 value += "${LIBWEBRTC_OUTPUT_DIR}/" + argument
474             elif (argument.startswith("-I../")):
475                 value += "-I${LIBWEBRTC_INPUT_DIR}/" + argument[5:]
476             elif (argument == "-I."):
477                 value += "-I${LIBWEBRTC_OUTPUT_DIR}"
478             elif (argument == "-I.."):
479                 value += "-I${LIBWEBRTC_INPUT_DIR}"
480             elif (argument == "-Igen"):
481                 value += "-I${LIBWEBRTC_OUTPUT_DIR}/gen"
482             else:
483                 value += argument
484         return value
485
486     def _generate_add_dependencies(self, name, target):
487         if not "deps" in target:
488             return []
489         dependencies = self.convert_deps([dep for dep in target["deps"] if self._is_active_dependency(dep)])
490         return ["add_dependencies(" + name + " " + dependencies + ")"] if len(dependencies) else []
491
492     def _is_active_dependency(self, name):
493         return not((self.skip_test_targets and self.targets[name]["testonly"]) or name.startswith("//webrtc/examples"))
494
495     def generate_action_target(self, name, target):
496         lines = []
497         outputs = self.convert_outputs(target["outputs"])
498         deps = self.convert_deps(target["deps"])
499         args = self.convert_arguments(target["args"])
500         script = "${LIBWEBRTC_INPUT_DIR}/" + target["script"][2:]
501         if (script.endswith(".py")):
502             script = "python " + script
503
504         lines.append("add_custom_command(OUTPUT " + outputs)
505         if deps:
506             lines.append("    DEPENDS " + deps)
507         lines.append("    COMMAND " + script + " " + args)
508         lines.append("    VERBATIM)")
509
510         lines.append("add_custom_target(" + name + " DEPENDS " + self.convert_deps(target["deps"]) + " " + self.convert_outputs(target["outputs"]) + ")")
511
512         return lines
513
514     def generate_action_foreach_target(self, name, target):
515         lines = []
516         outputs = [self.convert_output(output) for output in target["outputs"]]
517         deps = self.convert_deps(target["deps"])
518         sources = [self.convert_source(source) for source in target["sources"]]
519         script = "${LIBWEBRTC_INPUT_DIR}/" + target["script"][2:]
520         if (script.endswith(".py")):
521             script = "python " + script
522
523         for output, source in zip(outputs, sources):
524             args = self.convert_arguments(target["args"])
525             args = args.replace("{{source}}", source).replace("{{source_name_part}}", self.filesystem.splitext(self.filesystem.basename(source))[0])
526             lines.append("add_custom_command(OUTPUT " + output)
527             lines.append("    MAIN_DEPENDENCY " + source)
528             lines.append("    COMMAND " + script + " " + args)
529             if deps:
530                 lines.append("    DEPENDS " + deps)
531             lines.append("    VERBATIM)")
532
533         lines.append("add_custom_target(" + name + " DEPENDS " + " ".join(outputs) + ")")
534
535         return lines
536
537     def generate_copy_target(self, name, target):
538         lines = []
539         outputs = self.convert_outputs(target["outputs"])
540         sources = [self.convert_source(source) for source in target["sources"]]
541         lines.append("list(APPEND " + name + " " + outputs + ")")
542
543         for output, source in zip(target["outputs"], sources):
544             lines.append("file(COPY " + source + " DESTINATION " + self.convert_output(output) + ")")
545         lines.append("add_custom_target(" + name)
546         lines.append("    COMMAND echo \"Generating copy target" + name + "\"")
547         lines.append("    VERBATIM)")
548         lines.extend(self._generate_add_dependencies(name, target))
549         return lines
550
551     def _compute_compile_target_objects(self, name):
552         target = self.targets[name]
553         if target["type"] == "source_set" and not "sources" in target:
554             return []
555         sources = ["$<TARGET_OBJECTS:" + self.sanitize_target_name(name) + ">"]
556         for dep in self.targets[name]["deps"]:
557             if not self.targets[dep]["type"] == "source_set":
558                 continue
559             sources.extend(self._compute_compile_target_objects(dep))
560         return sources
561
562     def _compute_compile_target_sources(self, target):
563         sources = [self.convert_source(source) for source in target["sources"] if not source.endswith(".h")] if "sources" in target else []
564         if target["type"] == "source_set":
565             return sources
566
567         for dep in target["deps"]:
568             if not self.targets[dep]["type"] == "source_set":
569                 continue
570             sources.extend(self._compute_compile_target_objects(dep))
571
572         return sources
573
574     def _generate_compile_target_sources(self, name, target):
575         lines = []
576         sources = self._compute_compile_target_sources(target)
577         if len(sources):
578             lines.append("set(" + name + "_SOURCES " + "\n    ".join(sources) + ")")
579
580         return lines
581
582     def _compute_compile_flags(self, target):
583         flags = []
584         for flag in ["asmflags", "cflags", "cflags_c", "cflags_cc", "cflags_objc", "cflags_objcc"]:
585             if flag in target:
586                 flags.extend(target[flag])
587
588         self._remove_next_flag = False
589
590         def keep_flag(flag):
591             if self._remove_next_flag:
592                 self._remove_next_flag = False
593                 return False
594             if flag == "-Xclang":
595                 self._remove_next_flag = True
596                 return False
597             if flag == "-isysroot":
598                 self._remove_next_flag = True
599                 return False
600             if flag == "-Wno-undefined-var-template":
601                 return False
602             if flag == "-Wno-nonportable-include-path":
603                 return False
604             if flag == "-Wno-address-of-packed-member":
605                 return False
606             if flag == "-std=c++11":
607                 return False
608             return True
609         cleaned_flags = filter(keep_flag, flags)
610         no_duplicate_flags = []
611         [no_duplicate_flags.append(flag) for flag in cleaned_flags if not no_duplicate_flags.count(flag)]
612         return no_duplicate_flags
613
614     def compute_include_dirs(self, target):
615         dirs = []
616         if "include_dirs" in target:
617             dirs.extend(target["include_dirs"])
618         return dirs
619
620     def _generate_compile_target_options(self, name, target):
621         lines = []
622
623         flags = self._compute_compile_flags(target)
624         compilation_flags = "\" \"".join(flags)
625         lines.append("target_compile_options(" + name + " PRIVATE \"" + compilation_flags + "\")")
626
627         if "defines" in target:
628             lines.append("target_compile_definitions(" + name + " PRIVATE " + " ".join(target["defines"]) + ")")
629
630         dirs = list(set(self.compute_include_dirs(target)))
631         if len(dirs):
632             lines.append("target_include_directories(" + name + " PRIVATE " + self.convert_inputs(dirs) + ")")
633
634         if "ldflags" in target:
635             lines.append("set_target_properties(" + name + " PROPERTIES LINK_FLAGS \"" + " ".join(target["ldflags"]) + "\")")
636
637         return lines
638
639     def _compute_linked_libraries(self, target):
640         libraries = []
641         for dep in target["deps"]:
642             dep_target = self.targets[dep]
643             if dep_target["type"] == "static_library" or dep_target["type"] == "shared_library":
644                 libraries.append(self.sanitize_target_name(dep))
645             elif dep_target["type"] == "group" or dep_target["type"] == "source_set":
646                 libraries.extend(self._compute_linked_libraries(dep_target))
647         return libraries
648
649     def _generate_linked_libraries(self, name, target):
650         return [("target_link_libraries(" + name + " " + library + ")") for library in self._compute_linked_libraries(target)]
651
652     def _handle_frameworks(self, name, target):
653         if not "libs" in target:
654             return []
655
656         lines = []
657         for framework in target["libs"]:
658             framework = framework.replace(".framework", "").upper()
659             lines.append("target_include_directories(" + name + " PRIVATE ${" + framework + "_INCLUDE_DIR})")
660             lines.append("target_link_libraries(" + name + " ${" + framework + "_LIBRARY})")
661
662         return lines
663
664     def _set_output(self, name, target):
665         if not "outputs" in target:
666             return []
667
668         lines = []
669         output = target["outputs"][0]
670         if not output.startswith("//out/"):
671             raise "Output not in build directory"
672         output_dir = "${LIBWEBRTC_OUTPUT_DIR}/" + self.filesystem.dirname(output[6:])
673         output_name = self.filesystem.basename(output[6:])
674         if output_name.startswith("lib") and output_name.endswith(".a"):
675             output_name = output_name[3:-2]
676         lines.append("set_target_properties(" + name + " PROPERTIES RUNTIME_OUTPUT_DIRECTORY " + output_dir + ")")
677         lines.append("set_target_properties(" + name + " PROPERTIES OUTPUT_NAME " + output_name + ")")
678         return lines
679
680     def generate_executable_target(self, name, target):
681         lines = self._generate_compile_target_sources(name, target)
682         if len(lines):
683             lines.append("add_executable(" + name + " ${" + name + "_SOURCES})")
684         else:
685             lines.append("add_executable(" + name + ")")
686         lines.extend(self._generate_compile_target_options(name, target))
687
688         lines.extend(self._set_output(name, target))
689         lines.extend(self._generate_linked_libraries(name, target))
690         lines.extend(self._handle_frameworks(name, target))
691
692         lines.extend(self._generate_add_dependencies(name, target))
693         return lines
694
695     def generate_shared_library_target(self, name, target):
696         lines = self._generate_compile_target_sources(name, target)
697         if len(lines):
698             lines.append("add_library(" + name + " SHARED ${" + name + "_SOURCES})")
699         else:
700             lines.append("add_library(" + name + " SHARED)")
701         lines.extend(self._generate_compile_target_options(name, target))
702
703         lines.extend(self._set_output(name, target))
704         lines.extend(self._generate_linked_libraries(name, target))
705         lines.extend(self._handle_frameworks(name, target))
706
707         lines.extend(self._generate_add_dependencies(name, target))
708         return lines
709
710     def generate_static_library_target(self, name, target):
711         lines = self._generate_compile_target_sources(name, target)
712         lines.append("add_library(" + name + " STATIC" + ((" ${" + name + "_SOURCES}") if len(lines) else "") + ")")
713         lines.extend(self._generate_compile_target_options(name, target))
714
715         lines.extend(self._set_output(name, target))
716         lines.extend(self._generate_linked_libraries(name, target))
717         lines.extend(self._handle_frameworks(name, target))
718
719         return lines
720
721     def generate_bundle_data_target(self, name, target):
722         lines = []
723         lines.append("add_custom_target(" + name + ")")
724         lines.extend(self._generate_add_dependencies(name, target))
725         return lines
726
727     def generate_bundle_target(self, name, target):
728         # We replace dynamically Info.plist with a static one.
729         info_plist = "${LIBWEBRTC_INPUT_DIR}/../WebKit/" + self.filesystem.basename(target["bundle_data"]["source_files"][-1])
730         lines = self.generate_shared_library_target(name, target)
731         lines.append("set_target_properties(" + name + """ PROPERTIES
732             FRAMEWORK TRUE
733             FRAMEWORK_VERSION C
734             MACOSX_FRAMEWORK_INFO_PLIST """ + info_plist + ")")
735         return lines
736
737     def generate_group_target(self, name, target):
738         lines = []
739         lines.append("add_custom_target(" + name + ")")
740         lines.extend(self._generate_add_dependencies(name, target))
741         return lines
742
743     def generate_source_set_target(self, name, target):
744         if not "sources" in target or not len(target["sources"]):
745             return []
746
747         lines = self._generate_compile_target_sources(name, target)
748         if len(lines):
749             lines.append("add_library(" + name + " OBJECT ${" + name + "_SOURCES})")
750         else:
751             lines.append("add_library(" + name + " OBJECT)")
752         lines.extend(self._generate_compile_target_options(name, target))
753
754         return lines