blob: 207d32875413c004135f615b26a0955f00ed00a1 [file] [log] [blame]
// Copyright (C) 2015 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 main
import (
"flag"
"os"
"strings"
"sync"
"android.googlesource.com/platform/tools/gpu/build"
"android.googlesource.com/platform/tools/gpu/build/cpp"
"android.googlesource.com/platform/tools/gpu/build/cpp/gcc"
"android.googlesource.com/platform/tools/gpu/build/cpp/msvc"
"android.googlesource.com/platform/tools/gpu/build/cpp/ndk"
"android.googlesource.com/platform/tools/gpu/log"
)
var (
targets = flag.String("targets", build.HostOS, "A comma separated list of targets to build.")
buildtests = flag.Bool("buildtests", true, "Build the tests")
runtests = flag.Bool("runtests", true, "Run the tests after building the tests")
keystore = flag.String("keystore", GPURoot.Join("build", "keystore", "debug.keystore").Absolute(), "The keystore used to sign APKs")
storepass = flag.String("storepass", "android", "The password to the keystore")
keypass = flag.String("keypass", "android", "The password to the keystore's key")
keyalias = flag.String("alias", "androiddebugkey", "The alias of the key used to sign APKs")
logfile = flag.String("logfile", "", "Writes logging to a file instead of stdout")
forcebuild = flag.Bool("f", false, "All build steps will be forced")
verbose = flag.Bool("v", false, "Enable verbose logging")
debug = flag.Bool("d", false, "Generate debug binaries")
)
var (
ExternalRoot = build.RepoRoot.Path.Join("external")
GPURoot = build.RepoRoot.Path.Join("tools", "gpu", "src", "android.googlesource.com", "platform", "tools", "gpu")
BinRoot = build.RepoRoot.Path.Join("tools", "gpu", "bin")
CCRoot = GPURoot.Join("cc")
GapicRoot = CCRoot.Join("gapic")
GapiiRoot = CCRoot.Join("gapii")
GapirRoot = CCRoot.Join("gapir")
ReplaydRoot = CCRoot.Join("replayd")
GmockRoot = ExternalRoot.Join("gmock")
GtestRoot = ExternalRoot.Join("gtest")
)
func main() {
flag.Parse()
os.Exit(run())
}
func run() int {
var logger log.Logger
if len(*logfile) > 0 {
var err error
logger, err = log.File(*logfile)
if err != nil {
panic(err)
}
} else {
logger = log.Std()
}
defer logger.Close()
env := build.Environment{
Intermediates: build.RepoRoot.Path.Join("tools", "gpu", "pkg"),
Roots: build.RootList{
build.Root{Name: "external", Path: ExternalRoot},
build.Root{Name: "gpu", Path: GPURoot},
},
Keystore: build.File(*keystore),
Storepass: *storepass,
Keypass: *keypass,
Keyalias: *keyalias,
Logger: logger,
ForceBuild: *forcebuild,
Verbose: *verbose,
}
buildTargets := getBuildTargets()
targetNames := strings.Split(*targets, ",")
targets := make([]Target, len(targetNames))
for i, targetName := range targetNames {
if target, found := buildTargets[targetName]; found {
targets[i] = target
} else {
available := []string{}
for t := range buildTargets {
available = append(available, t)
}
logger.Errorf("Unknown target '%s'. Available targets: %v", targetName, available)
return 1
}
}
// Kick each of the targets on a separate go-routine.
wg := sync.WaitGroup{}
errors := make([]error, len(targets))
wg.Add(len(targets))
for i := range targets {
i := i
go func() {
env.Logger = logger.Fork().Enter(targetNames[i])
errors[i] = targets[i].Build(env)
wg.Done()
}()
}
// Wait for all go-routines to finish.
wg.Wait()
// Check for errors.
failed := false
for i := range targets {
targetName := targetNames[i]
if err := errors[i]; err != nil {
logger.Errorf("Target %v failed with error: %v", targetName, err)
failed = true
} else {
if *verbose {
logger.Infof("Target %v succeeded", targetName)
}
}
}
if failed {
return 1
}
return 0
}
type Target struct {
SourceFiles []string
Gtest cpp.Config
Gmock cpp.Config
Gapic 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),
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(env build.Environment) error {
rootLogger := env.Logger
begin := func(name string) log.Logger {
if env.Verbose {
rootLogger.Infof("Building %s", name)
}
return rootLogger.Enter(name)
}
var gtestLib, gmockLib build.File
if *buildtests {
var err error
// Build gtest into a library
env.Logger = begin(t.Gtest.Name)
gtestSource := GtestRoot.Join("src").Glob("gtest-all.cc", "gtest_main.cc")
gtestLib, err = cpp.StaticLibrary(gtestSource, t.Gtest, env)
if err != nil {
return err
}
// Build gmock into a library
env.Logger = begin(t.Gmock.Name)
gmockSource := GmockRoot.Join("src").Glob("gmock-all.cc")
gmockLib, err = cpp.StaticLibrary(gmockSource, t.Gmock, env)
if err != nil {
return err
}
}
// Gather the source files for gapic
gapicSource := GapicRoot.Glob(t.SourceFiles...).
Append(GapicRoot.Join(t.Gapic.OS).Glob(t.SourceFiles...)...).
Exclude("*_test.cpp")
// Build the gapic static library.
env.Logger = begin(t.Gapic.Name)
gapicLib, err := cpp.StaticLibrary(gapicSource, t.Gapic, env)
if err != nil {
return err
}
// Gather the source files for gapii
gapiiSource := GapiiRoot.Glob(t.SourceFiles...).
Append(GapiiRoot.Join(t.Gapic.OS).Glob(t.SourceFiles...)...).
Exclude("*_test.cpp")
// Build the gapii static library.
env.Logger = begin(t.Gapii.Name)
gapiiLib, err := cpp.StaticLibrary(gapiiSource, t.Gapii, env)
if err != nil {
return err
}
_ = gapiiLib
// Gather the source files for gapir
gapirSource := GapirRoot.Glob(t.SourceFiles...).
Append(GapirRoot.Join(t.Gapir.OS).Glob(t.SourceFiles...)...).
Exclude("*_test.cpp")
// Build the gapir static library.
env.Logger = begin(t.Gapir.Name)
gapirLib, err := cpp.StaticLibrary(gapirSource, t.Gapir, env)
if err != nil {
return err
}
// Build the spy from the gapii static library.
env.Logger = begin(t.Spy.Name)
// TODO: using the static-lib strips symbol visibility from the
// dynamic-library.
// Investigate linker flags to use build.Files(gapiiLib, gapicLib)
spyInputs := gapiiSource.Append(gapicLib)
if _, err := cpp.DynamicLibrary(spyInputs, t.Spy, env); err != nil {
return err
}
// Build replayd from the gapir static library and Main.cpp.
env.Logger = begin(t.Replayd.Name)
replaydSource := build.Files(ReplaydRoot.Join("main.cpp"))
replaydInputs := replaydSource.Append(gapirLib, gapicLib)
replayd, err := cpp.Executable(replaydInputs, t.Replayd, env)
if err != nil {
return err
}
// Build gapir tests.
if *buildtests {
env.Logger = begin(t.GapirTests.Name)
gapirTestSource := GapirRoot.Glob(t.SourceFiles...).
Append(GapirRoot.Join(t.Gapir.OS).Glob(t.SourceFiles...)...).
Filter("*_test.cpp")
gapirTestInputs := gapirTestSource.Append(gapirLib, gapicLib, gtestLib, gmockLib)
gapirTest, err := cpp.Executable(gapirTestInputs, t.GapirTests, env)
if err != nil {
return err
}
if *runtests && t.Replayd.OS == build.HostOS {
env.Logger = begin("Running gapir-tests")
if err := gapirTest.Exec(env); err != nil {
return err
}
}
}
if t.Replayd.OS == build.HostOS {
src, dst := replayd, BinRoot.Join(replayd.Name())
env.Logger = rootLogger
env.Logger.Infof("Copying %s -> %s", src, dst)
if err := src.CopyTo(dst); err != nil {
env.Logger.Errorf("Copy failed: %v", err)
}
}
return nil
}
func base(toolchain *cpp.Toolchain, os, architecture string) Target {
base := cpp.Config{
Toolchain: toolchain,
OS: os,
Architecture: architecture,
Defines: map[string]string{
"TARGET_OS_" + strings.ToUpper(os): "1",
},
}
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
}
base.OutputDir = BinRoot.Join(os+"-"+architecture, base.Flavor)
if toolchain == gcc.GCC {
base.LibrarySearchPaths = build.Files("/usr/local/lib")
}
if toolchain != msvc.MSVC {
base.Libraries = build.Files("stdc++")
base.CompilerArgs = []string{"-std=c++11"}
} else {
base.Flavor += "-msvc"
}
return Target{
SourceFiles: []string{"*.cpp", "*.cc"},
Gtest: base.Extend(cpp.Config{
Name: "gtest",
IncludeSearchPaths: build.FileSet{
GtestRoot,
GtestRoot.Join("include"),
},
}),
Gmock: base.Extend(cpp.Config{
Name: "gmock",
IncludeSearchPaths: build.FileSet{
GmockRoot,
GmockRoot.Join("include"),
GtestRoot,
GtestRoot.Join("include"),
},
}),
Gapic: base.Extend(cpp.Config{
Name: "gapic",
IncludeSearchPaths: build.FileSet{CCRoot},
}),
Gapii: base.Extend(cpp.Config{
Name: "gapii",
IncludeSearchPaths: build.FileSet{CCRoot},
}),
Gapir: base.Extend(cpp.Config{
Name: "gapir",
IncludeSearchPaths: build.FileSet{CCRoot},
}),
GapirTests: base.Extend(cpp.Config{
Name: "gapir-tests",
IncludeSearchPaths: build.FileSet{
CCRoot,
GmockRoot.Join("include"),
GtestRoot.Join("include"),
},
}),
Spy: base.Extend(cpp.Config{
Name: "spy",
IncludeSearchPaths: build.FileSet{CCRoot},
}),
Replayd: base.Extend(cpp.Config{
Name: "replayd",
IncludeSearchPaths: build.FileSet{
CCRoot,
},
Permissions: []string{
"android.permission.INTERNET",
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
},
}),
}
}
func getBuildTargets() map[string]Target {
noext := ""
linux := base(gcc.GCC, "linux", "x64").Extend(Target{
GapirTests: cpp.Config{
Libraries: build.FileSet{"dl", "GL", "m", "pthread", "X11", "rt"},
},
Replayd: cpp.Config{
Libraries: build.FileSet{"dl", "GL", "m", "pthread", "X11", "rt"},
},
Spy: cpp.Config{
Libraries: build.FileSet{"pthread"},
},
})
osx := base(gcc.GCC, "osx", "x64").Extend(Target{
SourceFiles: []string{"*.mm"},
Spy: cpp.Config{
Name: "OpenGL",
OutputExt: &noext,
},
GapirTests: cpp.Config{
LinkerArgs: []string{
"-framework", "Cocoa",
"-framework", "OpenGL",
},
Libraries: build.FileSet{"pthread"},
},
Replayd: cpp.Config{
LinkerArgs: []string{
"-framework", "Cocoa",
"-framework", "OpenGL",
},
Libraries: build.FileSet{"pthread"},
},
})
osx.Spy.OutputDir = osx.Spy.OutputDir.Join("OpenGL.framework", "Versions", "A")
windows := base(gcc.GCC, "windows", "x64").Extend(Target{
Spy: cpp.Config{
Libraries: build.FileSet{"ws2_32", "opengl32", "gdi32", "user32"},
},
GapirTests: cpp.Config{
Libraries: build.FileSet{"ws2_32", "opengl32", "gdi32", "user32"},
},
Replayd: cpp.Config{
Libraries: build.FileSet{"ws2_32", "opengl32", "gdi32", "user32"},
},
})
windows_msvc := base(msvc.MSVC, "windows", "x64").Extend(Target{
Spy: cpp.Config{
Name: "opengl32",
AdditionalSources: build.FileSet{GapiiRoot.Join("windows", "opengl32_x64.asm")},
ModuleDefinition: GapiiRoot.Join("windows", "opengl32_exports.def"),
Libraries: build.FileSet{"ws2_32", "opengl32", "gdi32", "user32"},
},
GapirTests: cpp.Config{
Libraries: build.FileSet{"ws2_32", "opengl32", "gdi32", "user32"},
},
Replayd: cpp.Config{
Libraries: build.FileSet{"ws2_32", "opengl32", "gdi32", "user32"},
},
})
android_target := Target{
GapirTests: cpp.Config{
Toolchain: ndk.EXE,
Libraries: build.FileSet{"EGL", "log", "android", "z", "m"},
},
Replayd: cpp.Config{
Libraries: build.FileSet{"EGL", "log", "android", "z", "m"},
IncludeSearchPaths: build.FileSet{ndkRoot().Join("sources", "android", "native_app_glue")},
AdditionalSources: build.FileSet{ndkRoot().Join("sources", "android", "native_app_glue", "android_native_app_glue.c")},
},
Spy: cpp.Config{
Libraries: build.FileSet{"log", "z", "m", "dl"},
},
}
android_arm := base(ndk.APK, "android", "arm").Extend(android_target)
android_arm64 := base(ndk.APK, "android", "arm64").Extend(android_target)
return map[string]Target{
"linux": linux,
"osx": osx,
"windows": windows,
"windows-msvc": windows_msvc,
"android-arm": android_arm,
"android-arm64": android_arm64,
}
}
func ndkRoot() build.File {
paths, _ := ndk.ResolvePaths()
return paths.NDK
}