blob: 0d0f2f85a06567e2eac4ab71c38239a246bd04e7 [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 cpp
import (
"path/filepath"
"android.googlesource.com/platform/tools/gpu/build"
"android.googlesource.com/platform/tools/gpu/log"
"android.googlesource.com/platform/tools/gpu/maker"
)
// Add steps to the "maker" build system for compiling, linking of c++.
// Target for generating generated code
const code = "code"
var sourcePatterns = []string{"*.cpp", "*.c", "*.cc", "*.mm", "*.asm"}
func makeEntity(f build.File) maker.Entity {
return maker.File(f.Absolute())
}
func makeEntities(fs build.FileSet) []interface{} {
var es []interface{}
for _, f := range fs {
es = append(es, makeEntity(f))
}
return es
}
func makeStep(name string, output build.File, source build.FileSet, always bool, a func(*maker.Step) error) *maker.Step {
s := maker.NewStep(a)
e := makeEntity(output)
s.Creates(e)
s.DependsOn(maker.DirOf(e))
s.DependsOn(makeEntities(source)...)
// This is a bit sad, but codergen does not know what its outputs are.
s.DependsOn(code)
if always {
s.AlwaysRun()
}
if name != "" {
maker.List("cc:" + name).DependsOn(e)
}
maker.List("cc").DependsOn(e)
return s
}
func logger(env build.Environment, name string) log.Logger {
rootLogger := env.Logger
if env.Verbose {
log.Infof(rootLogger, "Building %s", name)
}
return log.Enter(rootLogger, name)
}
// Add make steps to compile all the specified source files.
// Returns fileset of the resulting object files.
// Note we do not get a dependency on the compiler.
func MakeCompile(sources build.FileSet, cfg Config, env build.Environment) build.FileSet {
ext := cfg.Toolchain.ObjExt(cfg)
if cfg.OutputExt != nil {
ext = *cfg.OutputExt
}
sources = sources.Append(cfg.AdditionalSources...)
objects := make([]build.File, len(sources))
for i, source := range sources {
i, source, env := i, source, env
object := IntermediatePath(source, ext, cfg, env)
objects[i] = object
prev := maker.Creator(makeEntity(object))
if prev != nil {
// skip any objects which have existing build steps
continue
}
s := makeStep("", object, build.Files(source), env.ForceBuild,
func(*maker.Step) error {
env.Logger = log.Enter(env.Logger, object.Name())
env.Logger = log.Enter(env.Logger, "C++.Compile")
return cfg.Toolchain.Compiler(source, object, cfg, env)
})
depsFor := cfg.Toolchain.DepsFor
if depsFor == nil {
// If the toolchain can't check dependencies, then we have to build
s.DependsOn(code)
s.AlwaysRun()
continue
}
deps, valid := depsFor(object, cfg, env)
if !valid {
depFileFor := cfg.Toolchain.DepFileFor
if depFileFor != nil {
s.DependsOn(maker.DirOf(depFileFor(object, cfg, env).Absolute()))
}
s.DependsOn(code)
s.AlwaysRun()
continue
}
entities := makeEntities(deps)
for _, dep := range entities {
e := maker.EntityOf(dep)
if e != nil && e.Timestamp().IsZero() {
// If any dependencies are missing then do code generation
s.DependsOn(code)
break
}
}
s.DependsOn(entities...)
}
return objects
}
// Add make steps to construct a static library. The inputs may be a
// combination of source and / or object files. Source file inputs will
// generate compilation steps. Returns the output library name.
// Note we do not get a dependency on the archiver.
func MakeStaticLibrary(inputs build.FileSet, cfg Config, env build.Environment) build.File {
objects := MakeCompile(inputs.Filter(sourcePatterns...), cfg, env)
objects = objects.Append(inputs.Filter("*" + cfg.Toolchain.ObjExt(cfg))...)
name := cfg.Toolchain.LibName(cfg)
output := env.Intermediates.Join(Triplet(cfg), name).ChangeExt(cfg.Toolchain.LibExt(cfg))
if cfg.OutputExt != nil {
output = output.ChangeExt(*cfg.OutputExt)
}
makeStep("", output, objects, env.ForceBuild,
func(*maker.Step) error {
env.Logger = log.Enter(logger(env, cfg.Name), "C++.StaticLibrary")
return cfg.Toolchain.Archiver(objects, output, cfg, env)
})
return output
}
// Add make steps to construct a dynamic library. The inputs can be a
// combination of source files, object files and / or library files.
// Source file inputs will generate compilation steps.
// Returns the output library name.
// Note we do not get a dependency on the linker.
func MakeDynamicLibrary(name string, inputs build.FileSet, cfg Config, env build.Environment) build.File {
objects := MakeCompile(inputs.Filter(sourcePatterns...), cfg, env)
objects = objects.Append(inputs.Filter("*" + cfg.Toolchain.ObjExt(cfg))...)
libs := inputs.Filter("*" + cfg.Toolchain.LibExt(cfg))
libraries := build.FileSet{}
for _, library := range libs {
dir, base := filepath.Split(library.Absolute())
libraries = libraries.Append(build.File(base))
cfg.LibrarySearchPaths = cfg.LibrarySearchPaths.Append(build.File(dir))
}
cfg.Libraries = append(libraries, cfg.Libraries...)
output := cfg.OutputDir.Join(cfg.Toolchain.DllName(cfg))
if cfg.OutputExt != nil {
output = output.ChangeExt(*cfg.OutputExt)
}
deps := libs.Append(objects...)
makeStep(name, output, deps, env.ForceBuild,
func(*maker.Step) error {
env.Logger = log.Enter(logger(env, cfg.Name), "C++.DynamicLibrary")
return cfg.Toolchain.DllLinker(objects, output, cfg, env)
})
return output
}
// Add make steps to construct an executable. The inputs can be a combination
// of source files, object files and / or library files. Source file inputs
// will generate compilation steps. Returns the output library name.
// Note we do not get a dependency on the linker.
func MakeExecutable(name string, inputs build.FileSet, cfg Config, env build.Environment) build.File {
objects := MakeCompile(inputs.Filter(sourcePatterns...), cfg, env)
objects = objects.Append(inputs.Filter("*" + cfg.Toolchain.ObjExt(cfg))...)
libs := inputs.Filter("*" + cfg.Toolchain.LibExt(cfg))
libraries := build.FileSet{}
for _, library := range libs {
dir, base := filepath.Split(library.Absolute())
libraries = libraries.Append(build.File(base))
cfg.LibrarySearchPaths = cfg.LibrarySearchPaths.Append(build.File(dir))
}
cfg.Libraries = append(libraries, cfg.Libraries...)
output := cfg.OutputDir.Join(cfg.Toolchain.ExeName(cfg))
if cfg.OutputExt != nil {
output = output.ChangeExt(*cfg.OutputExt)
}
deps := libs.Append(objects...)
makeStep(name, output, deps, env.ForceBuild,
func(*maker.Step) error {
env.Logger = log.Enter(logger(env, cfg.Name), "C++.Executable")
return cfg.Toolchain.ExeLinker(objects, output, cfg, env)
})
return output
}
// Add a make step to run a test. The test only runs on the host OS.
// The test is automatically included in the "test" target.
func MakeRunTest(test build.File, cfg Config) {
// We only run tests on the host OS.
if cfg.OS == maker.HostOS {
// cfg.Defines["LOG_LEVEL"] = "0" // Disable all log messages except for GAPID_FATAL.
phony := maker.Virtual("cc:" + test.Name() + ":run")
maker.Command(makeEntity(test)).Creates(phony)
maker.List("cc_test").DependsOn(phony)
}
}
// Add a make step to copy a file from "src" to "dest". The copy only
// happens on the host OS.
func MakeCopy(src build.File, dest build.File, cfg Config, env build.Environment) {
logger := env.Logger
if cfg.OS == maker.HostOS {
makeStep(cfg.Name, dest, build.Files(src), env.ForceBuild,
func(*maker.Step) error {
log.Infof(logger, "Copying %s -> %s", src, dest)
if err := src.CopyTo(dest); err != nil {
log.Errorf(logger, "Copy failed: %v", err)
// Error delibrately suppressed
}
return nil
}).AlwaysRun()
}
}