| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package cc |
| |
| import ( |
| "flag" |
| "fmt" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "android.googlesource.com/platform/tools/gpu/framework/device" |
| "android.googlesource.com/platform/tools/gpu/framework/file" |
| "android.googlesource.com/platform/tools/gpu/framework/log" |
| "android.googlesource.com/platform/tools/gpu/tools/maker/config" |
| "android.googlesource.com/platform/tools/gpu/tools/maker/cpp" |
| "android.googlesource.com/platform/tools/gpu/tools/maker/cpp/gcc" |
| "android.googlesource.com/platform/tools/gpu/tools/maker/cpp/msvc" |
| "android.googlesource.com/platform/tools/gpu/tools/maker/cpp/ndk" |
| "android.googlesource.com/platform/tools/gpu/tools/maker/graph" |
| ) |
| |
| var ( |
| logfile = flag.String("logfile", "", "Writes logging to a file instead of stdout") |
| debug = flag.Bool("d", false, "Generate debug binaries") |
| ) |
| |
| func getLogger() log.Context { |
| handler := log.Std(log.Normal) |
| if len(*logfile) > 0 { |
| os.MkdirAll(filepath.Dir(*logfile), 0755) |
| file, err := os.Create(*logfile) |
| if err != nil { |
| panic(err) |
| } |
| handler = log.Writer(log.Normal, file) |
| } |
| handler, _ = log.Channel(handler) |
| return log.Background().PreFilter(log.Limit(log.NoticeLevel)).Filter(log.Pass).Handler(handler) |
| } |
| |
| type Target struct { |
| Name string |
| SourceFiles []string |
| Gtest cpp.Config |
| Gmock cpp.Config |
| Gapic cpp.Config |
| GapicTests cpp.Config |
| Gapii cpp.Config |
| Gapir cpp.Config |
| GapirTests cpp.Config |
| Spy cpp.Config |
| Replayd cpp.Config |
| } |
| |
| func (t Target) Extend(n Target) Target { |
| return Target{ |
| SourceFiles: append(append([]string{}, t.SourceFiles...), n.SourceFiles...), |
| Gtest: t.Gtest.Extend(n.Gtest), |
| Gmock: t.Gmock.Extend(n.Gmock), |
| Gapic: t.Gapic.Extend(n.Gapic), |
| GapicTests: t.GapicTests.Extend(n.GapicTests), |
| Gapii: t.Gapii.Extend(n.Gapii), |
| Gapir: t.Gapir.Extend(n.Gapir), |
| GapirTests: t.GapirTests.Extend(n.GapirTests), |
| Spy: t.Spy.Extend(n.Spy), |
| Replayd: t.Replayd.Extend(n.Replayd), |
| } |
| } |
| |
| func (t Target) Build(ctx log.Context) { |
| ctx = ctx.Enter(t.Name) |
| // Build gtest into a library |
| gtestSource := config.Paths.GtestRoot.Join("src").Glob("gtest-all.cc", "gtest_main.cc").AsSet() |
| gtestLib := cpp.MakeStaticLibrary(ctx, gtestSource, t.Gtest, true) |
| |
| // Build gmock into a library |
| gmockSource := config.Paths.GmockRoot.Join("src").Glob("gmock-all.cc").AsSet() |
| gmockLib := cpp.MakeStaticLibrary(ctx, gmockSource, t.Gmock, true) |
| |
| // Gather the source files for gapic |
| gapicSource := config.Paths.GapicRoot.Glob(t.SourceFiles...). |
| Append(config.Paths.GapicRoot.Join("gl").Glob(t.SourceFiles...)...). |
| Append(config.Paths.GapicRoot.Join(strings.ToLower(t.Gapic.Target.ABI.OS.String())).Glob(t.SourceFiles...)...). |
| NotMatching("*_test.cpp").AsSet().Union(t.Gapic.AdditionalSources) |
| |
| // Build the gapic static library. |
| gapicLib := cpp.MakeStaticLibrary(ctx, gapicSource, t.Gapic, true) |
| |
| // Gather the source files for gapii |
| gapiiSource := config.Paths.GapiiRoot.Glob(t.SourceFiles...). |
| Append(config.Paths.GapiiRoot.Join(strings.ToLower(t.Gapii.Target.ABI.OS.String())).Glob(t.SourceFiles...)...). |
| NotMatching("*_test.cpp"). |
| NotMatching("link_interceptor.cpp"). // only need in the .so |
| // Generated by APIC |
| Append(config.Paths.GapiiRoot.Join("gles_imports.cpp")). |
| Append(config.Paths.GapiiRoot.Join("gles_exports.cpp")). |
| AsSet().Union(gapicSource) |
| for i := 0; i < 7; i++ { |
| gapiiSource.Append(config.Paths.GapiiRoot.Join(fmt.Sprintf("gles_spy_%d.cpp", i))) |
| } |
| for i := 0; i < 2; i++ { |
| gapiiSource.Append(config.Paths.GapiiRoot.Join(fmt.Sprintf("gles_spy_subroutines_%d.cpp", i))) |
| } |
| |
| // Gather the source files for gapir |
| gapirSource := config.Paths.GapirRoot.Glob(t.SourceFiles...). |
| Append(config.Paths.GapirRoot.Join(strings.ToLower(t.Gapir.Target.ABI.OS.String())).Glob(t.SourceFiles...)...). |
| NotMatching("*_test.cpp"). |
| // Generated by APIC |
| Append(config.Paths.GapirRoot.Join("gfx_api.cpp")).AsSet() |
| |
| // Build the gapir static library. |
| gapirLib := cpp.MakeStaticLibrary(ctx, gapirSource, t.Gapir, true) |
| |
| // Build the spy (libgapii.so) from the gapii source files. |
| // TODO Investigate linker flags to use to check for undefined symbols |
| gapiiSo := cpp.MakeDynamicLibrary(ctx, "spy", gapiiSource, t.Spy) |
| |
| if t.Replayd.Target.ABI.OS == device.OSKind_ANDROID { |
| // Copy: bin/android-<abi>/<debug/release>/libgapii.so -> bin/android-<abi>/libgapii.so |
| cpp.MakeCopy(ctx, gapiiSo, t.Spy.OutputDir.Join("..", gapiiSo.Basename()), t.Spy) |
| } |
| |
| // Build gapir from the gapir static library and Main.cpp. |
| replaydSource := file.Paths(config.Paths.ReplaydRoot.Join("main.cpp")).AsSet() |
| replaydInputs := replaydSource.Append(gapirLib, gapicLib) |
| replayd := cpp.MakeExecutable(ctx, "gapir", replaydInputs, t.Replayd) |
| |
| // Build tests. |
| gapicTestSource := config.Paths.GapicRoot.Glob(t.SourceFiles...). |
| Append(config.Paths.GapicRoot.Join(strings.ToLower(t.Gapic.Target.ABI.OS.String())).Glob(t.SourceFiles...)...). |
| Matching("*_test.cpp").AsSet() |
| gapicTestInputs := gapicTestSource.Append(gtestLib, gmockLib, gapicLib) |
| gapicTest := cpp.MakeExecutable(ctx, "gapic-tests", gapicTestInputs, t.GapicTests) |
| |
| gapirTestSource := config.Paths.GapirRoot.Glob(t.SourceFiles...). |
| Append(config.Paths.GapirRoot.Join(strings.ToLower(t.Gapir.Target.ABI.OS.String())).Glob(t.SourceFiles...)...). |
| Matching("*_test.cpp").AsSet() |
| gapirTestInputs := gapirTestSource.Append(gtestLib, gmockLib, gapirLib, gapicLib) |
| gapirTest := cpp.MakeExecutable(ctx, "gapir-tests", gapirTestInputs, t.GapirTests) |
| |
| if t.Replayd.Target.ABI.OS == config.HostOS { |
| cpp.MakeRunTest(ctx, gapicTest, t.GapicTests) |
| |
| cpp.MakeRunTest(ctx, gapirTest, t.GapirTests) |
| |
| src, dst := replayd, config.Paths.BinRoot.Join(replayd.Basename()) |
| cpp.MakeCopy(ctx, src, dst, t.Replayd) |
| } |
| |
| if config.HostOS == device.OSKind_LINUX { |
| deqpTest := config.Paths.GPURoot.Join("tools/deqp.sh") |
| cpp.MakeRunTest(ctx, deqpTest, t.Gapii) |
| graph.List(ctx, graph.Virtual("cc:deqp.sh:run")).DependsOn(gapiiSo) |
| } |
| } |
| |
| func base(toolchain *cpp.Toolchain, target *config.Target) Target { |
| defs := map[string]string{ |
| "TARGET_OS_" + strings.ToUpper(target.ABI.OS.String()): "1", |
| } |
| base := cpp.Config{ |
| Toolchain: toolchain, |
| Target: target, |
| Defines: defs, |
| } |
| |
| if *debug { |
| base.Flavor = "debug" |
| base.OptimizationLevel = cpp.NoOptimization |
| base.Defines["LOG_LEVEL"] = "3" // LOG_LEVEL_DEBUG |
| } else { |
| base.Flavor = "release" |
| base.OptimizationLevel = cpp.FullOptimization |
| } |
| |
| if toolchain == gcc.GCC { |
| base.LibrarySearchPaths = file.Paths(file.Abs("/usr/local/lib")).AsSet() |
| } |
| |
| if toolchain != msvc.MSVC { |
| base.Libraries = []string{"stdc++"} |
| base.CompilerArgs = []string{"-std=c++11"} |
| } else { |
| base.Flavor += "-msvc" |
| } |
| |
| base.OutputDir = config.Paths.BinRoot.Join(strings.ToLower(target.ABI.OS.String()+"-"+target.ABI.Architecture.String()), base.Flavor) |
| |
| return Target{ |
| Name: target.ABI.Name, |
| SourceFiles: []string{"*.cpp", "*.cc"}, |
| Gtest: base.Extend(cpp.Config{ |
| Name: "gtest", |
| IncludeSearchPaths: file.Paths( |
| config.Paths.GtestRoot, |
| config.Paths.GtestRoot.Join("include"), |
| ).AsSet(), |
| }), |
| Gmock: base.Extend(cpp.Config{ |
| Name: "gmock", |
| IncludeSearchPaths: file.Paths( |
| config.Paths.GmockRoot, |
| config.Paths.GmockRoot.Join("include"), |
| config.Paths.GtestRoot, |
| config.Paths.GtestRoot.Join("include"), |
| ).AsSet(), |
| }), |
| Gapic: base.Extend(cpp.Config{ |
| Name: "gapic", |
| AdditionalSources: file.Paths( |
| config.Paths.GapicRoot.Join("coder").Glob("*.cpp")...).AsSet(), |
| IncludeSearchPaths: file.Paths(config.Paths.CCRoot).AsSet(), |
| }), |
| GapicTests: base.Extend(cpp.Config{ |
| Name: "gapic-tests", |
| IncludeSearchPaths: file.Paths( |
| config.Paths.CCRoot, |
| config.Paths.GmockRoot.Join("include"), |
| config.Paths.GtestRoot.Join("include"), |
| ).AsSet(), |
| }), |
| Gapii: base.Extend(cpp.Config{ |
| Name: "gapii", |
| IncludeSearchPaths: file.Paths(config.Paths.CCRoot).AsSet(), |
| }), |
| Gapir: base.Extend(cpp.Config{ |
| Name: "gapir", |
| IncludeSearchPaths: file.Paths(config.Paths.CCRoot).AsSet(), |
| }), |
| GapirTests: base.Extend(cpp.Config{ |
| Name: "gapir-tests", |
| IncludeSearchPaths: file.Paths( |
| config.Paths.CCRoot, |
| config.Paths.GmockRoot.Join("include"), |
| config.Paths.GtestRoot.Join("include"), |
| ).AsSet(), |
| }), |
| Spy: base.Extend(cpp.Config{ |
| Name: "gapii", |
| IncludeSearchPaths: file.Paths(config.Paths.CCRoot).AsSet(), |
| }), |
| Replayd: base.Extend(cpp.Config{ |
| Name: "gapir", |
| IncludeSearchPaths: file.Paths(config.Paths.CCRoot).AsSet(), |
| }), |
| } |
| } |
| |
| // Generate "maker" graph for building the cpp code. |
| func Graph() { |
| // Note this logger is captured in the build graph. There is no good place |
| // to call logger.Close() |
| ctx := getLogger() |
| |
| noext := "" |
| |
| switch config.TargetOS { |
| case device.OSKind_LINUX: |
| base(gcc.GCC, config.GetTarget(config.TargetOS, device.Architecture_X86_64)).Extend(Target{ |
| GapicTests: cpp.Config{ |
| Libraries: []string{"pthread"}, |
| }, |
| GapirTests: cpp.Config{ |
| Libraries: []string{"dl", "GL", "m", "pthread", "X11", "rt"}, |
| }, |
| Replayd: cpp.Config{ |
| Libraries: []string{"dl", "GL", "m", "pthread", "X11", "rt"}, |
| }, |
| Spy: cpp.Config{ |
| Name: "libgapii", |
| Libraries: []string{"pthread"}, |
| }, |
| }).Build(ctx) |
| |
| case device.OSKind_OSX: |
| osx := base(gcc.GCC, config.GetTarget(config.TargetOS, device.Architecture_X86_64)).Extend(Target{ |
| SourceFiles: []string{"*.mm"}, |
| Gapii: cpp.Config{ |
| AdditionalSources: file.Paths( |
| config.Paths.GapiiRoot.Join("osx", "opengl_framework_exports.cpp")).AsSet(), |
| }, |
| Spy: cpp.Config{ |
| Name: "OpenGL", |
| OutputExt: &noext, |
| }, |
| GapirTests: cpp.Config{ |
| LinkerArgs: []string{ |
| "-framework", "Cocoa", |
| "-framework", "OpenGL", |
| }, |
| Libraries: []string{"pthread"}, |
| }, |
| Replayd: cpp.Config{ |
| LinkerArgs: []string{ |
| "-framework", "Cocoa", |
| "-framework", "OpenGL", |
| }, |
| Libraries: []string{"pthread"}, |
| }, |
| }) |
| osx.Spy.OutputDir = osx.Spy.OutputDir.Join("OpenGL.framework", "Versions", "A") |
| osx.Build(ctx) |
| |
| case device.OSKind_WINDOWS: |
| base(gcc.GCC, config.GetTarget(config.TargetOS, device.Architecture_X86_64)).Extend(Target{ |
| Gapii: cpp.Config{ |
| AdditionalSources: file.Paths( |
| config.Paths.GapiiRoot.Join("windows", "opengl32_resolve.cpp")).AsSet(), |
| }, |
| Spy: cpp.Config{ |
| Libraries: []string{"ws2_32", "opengl32", "gdi32", "user32"}, |
| }, |
| GapirTests: cpp.Config{ |
| Libraries: []string{"ws2_32", "opengl32", "gdi32", "user32"}, |
| }, |
| Replayd: cpp.Config{ |
| Libraries: []string{"ws2_32", "opengl32", "gdi32", "user32"}, |
| }, |
| }).Build(ctx) |
| if config.HostOS == device.OSKind_WINDOWS { |
| base(msvc.MSVC, config.GetTarget(config.HostOS, device.Architecture_X86_64)).Extend(Target{ |
| Gapii: cpp.Config{ |
| AdditionalSources: file.Paths( |
| config.Paths.GapiiRoot.Join("windows", "opengl32_resolve.cpp")).AsSet(), |
| }, |
| Spy: cpp.Config{ |
| Name: "opengl32", |
| AdditionalSources: file.Paths(config.Paths.GapiiRoot.Join("windows", "opengl32_x64.asm")).AsSet(), |
| ModuleDefinition: config.Paths.GapiiRoot.Join("windows", "opengl32_exports.def"), |
| Libraries: []string{"ws2_32", "opengl32", "gdi32", "user32"}, |
| }, |
| GapirTests: cpp.Config{ |
| Libraries: []string{"ws2_32", "opengl32", "gdi32", "user32"}, |
| }, |
| Replayd: cpp.Config{ |
| Libraries: []string{"ws2_32", "opengl32", "gdi32", "user32"}, |
| }, |
| }).Build(ctx) |
| } |
| } |
| |
| // A bit of the Android crazy linker from the NDK. This is used to |
| // relink the program to use spy interceptors on non-rooted devices. |
| crazy := config.Paths.CCRoot.Join("third_party", "ndk", "crazy_linker", "src") |
| crazy_source := file.Paths( |
| crazy.Join("crazy_linker_elf_view.cpp"), |
| crazy.Join("crazy_linker_error.cpp"), |
| crazy.Join("linker_phdr.cpp"), |
| ).AsSet() |
| |
| app_glue := config.Paths.CCRoot.Join("third_party", "ndk", "native_app_glue") |
| |
| android_target := Target{ |
| GapicTests: cpp.Config{ |
| Toolchain: ndk.EXE, |
| Libraries: []string{"log", "android", "z", "m"}, |
| }, |
| GapirTests: cpp.Config{ |
| Toolchain: ndk.EXE, |
| Libraries: []string{"EGL", "log", "android", "z", "m"}, |
| }, |
| Replayd: cpp.Config{ |
| Libraries: []string{"EGL", "log", "android", "z", "m"}, |
| IncludeSearchPaths: file.Paths(app_glue.Join()).AsSet(), |
| AdditionalSources: file.Paths(app_glue.Join("android_native_app_glue.c")).AsSet(), |
| }, |
| Spy: cpp.Config{ |
| Libraries: []string{"log", "z", "m", "dl"}, |
| IncludeSearchPaths: file.Paths(crazy).AsSet(), |
| AdditionalSources: crazy_source.Append( |
| config.Paths.GapiiRoot.Join("android", "link_interceptor.cpp")), |
| }, |
| } |
| base(ndk.APK, config.GetTarget(device.OSKind_ANDROID, device.Architecture_ARMv7a)).Extend(android_target).Build(ctx) |
| base(ndk.APK, config.GetTarget(device.OSKind_ANDROID, device.Architecture_ARMv8a)).Extend(android_target).Build(ctx) |
| // uncomment the following line for building Android x86 targets for emulator, if you configured x86 compiler |
| //base(ndk.APK, config.GetTarget(device.OSKind_ANDROID, device.Architecture_X86)).Extend(android_target).Build(ctx) |
| } |