[tint] Add build support for protobufs
Change-Id: I764ecca72d93f35e4cb7478df5887a0632738226
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/161001
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 3a47bd1..052ab20 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -177,6 +177,26 @@
target_include_directories(${TARGET} PRIVATE "${TINT_SPIRV_TOOLS_DIR}/include")
endfunction()
+function(tint_lib_compile_options TARGET)
+ if (TINT_ENABLE_INSTALL)
+ install(TARGETS ${TARGET}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ )
+ endif()
+ tint_default_compile_options(${TARGET})
+endfunction()
+
+function(tint_proto_compile_options TARGET)
+ if (TINT_ENABLE_INSTALL)
+ install(TARGETS ${TARGET}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ )
+ endif()
+ tint_core_compile_options(${TARGET})
+endfunction()
+
function(tint_test_compile_options TARGET)
tint_default_compile_options(${TARGET})
set_target_properties(${TARGET} PROPERTIES FOLDER "Tests")
@@ -324,7 +344,7 @@
if(TINT_BUILD_CMD_TOOLS)
set(IS_ENABLED TRUE PARENT_SCOPE)
endif()
- elseif(${KIND} STREQUAL lib)
+ elseif((${KIND} STREQUAL lib) OR (${KIND} STREQUAL proto))
set(IS_ENABLED TRUE PARENT_SCOPE)
elseif((${KIND} STREQUAL test) OR (${KIND} STREQUAL test_cmd))
if(TINT_BUILD_TESTS)
@@ -363,13 +383,12 @@
if(${KIND} STREQUAL lib)
add_library(${TARGET} STATIC EXCLUDE_FROM_ALL)
- if (TINT_ENABLE_INSTALL)
- install(TARGETS ${TARGET}
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
- )
- endif()
- tint_default_compile_options(${TARGET})
+ tint_lib_compile_options(${TARGET})
+ elseif(${KIND} STREQUAL proto)
+ add_library(${TARGET} STATIC EXCLUDE_FROM_ALL)
+ list(APPEND TINT_PROTO_TARGETS ${TARGET})
+ set(TINT_PROTO_TARGETS ${TINT_PROTO_TARGETS} PARENT_SCOPE)
+ tint_proto_compile_options(${TARGET})
elseif(${KIND} STREQUAL cmd)
add_executable(${TARGET})
tint_default_compile_options(${TARGET})
@@ -611,6 +630,19 @@
################################################################################
+# Generate protobuf sources
+################################################################################
+foreach(PROTO_TARGET ${TINT_PROTO_TARGETS})
+ generate_protos(
+ TARGET ${PROTO_TARGET}
+ PROTOC_OUT_DIR "${DAWN_BUILD_GEN_DIR}/src/tint/")
+ target_include_directories(${PROTO_TARGET} PRIVATE "${DAWN_BUILD_GEN_DIR}/src/tint/")
+ target_include_directories(${PROTO_TARGET} PUBLIC "${DAWN_BUILD_GEN_DIR}")
+ target_link_libraries(${PROTO_TARGET} libprotobuf)
+endforeach()
+
+
+################################################################################
# Bespoke target settings
################################################################################
if (MSVC)
diff --git a/src/tint/tint.gni b/src/tint/tint.gni
index 137def3..d40c570 100644
--- a/src/tint/tint.gni
+++ b/src/tint/tint.gni
@@ -27,8 +27,13 @@
import("//build_overrides/build.gni")
+import("//third_party/protobuf/proto_library.gni")
+
import("../../scripts/tint_overrides_with_defaults.gni")
+###############################################################################
+# Tint library target
+###############################################################################
template("libtint_source_set") {
source_set(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
@@ -57,6 +62,18 @@
}
###############################################################################
+# Tint protobuf library target
+###############################################################################
+template("tint_proto_library") {
+ proto_library(target_name) {
+ forward_variables_from(invoker, "*", [ "configs" ])
+ generate_cc = true
+ generate_python = false
+ use_protobuf_full = true
+ }
+}
+
+###############################################################################
# Executables - only built when tint_build_cmds is enabled
###############################################################################
template("tint_executable") {
diff --git a/tools/src/cmd/gen/build/BUILD.cmake.tmpl b/tools/src/cmd/gen/build/BUILD.cmake.tmpl
index 66250f4..9e8412d 100644
--- a/tools/src/cmd/gen/build/BUILD.cmake.tmpl
+++ b/tools/src/cmd/gen/build/BUILD.cmake.tmpl
@@ -1,5 +1,6 @@
{{- Eval "Includes" $}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "lib")}}
+{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "proto")}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "cmd")}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "test_cmd")}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "bench_cmd")}}
diff --git a/tools/src/cmd/gen/build/BUILD.gn.tmpl b/tools/src/cmd/gen/build/BUILD.gn.tmpl
index 128309b..6e156f2 100644
--- a/tools/src/cmd/gen/build/BUILD.gn.tmpl
+++ b/tools/src/cmd/gen/build/BUILD.gn.tmpl
@@ -13,7 +13,9 @@
}
{{- end}}
+
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "lib")}}
+{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "proto")}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "cmd")}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "test")}}
{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "test_cmd")}}
@@ -55,6 +57,8 @@
{{ if $.Kind.IsLib -}}
libtint_source_set("{{$.Directory.Name}}") {
+{{- else if $.Kind.IsProto -}}
+tint_proto_library("proto") {
{{- else if $.Kind.IsCmd -}}
tint_executable("{{$.Directory.Name}}") {
{{- else if $.Kind.IsTest -}}
diff --git a/tools/src/cmd/gen/build/build.go b/tools/src/cmd/gen/build/build.go
index 3f09d29..db2bbc0 100644
--- a/tools/src/cmd/gen/build/build.go
+++ b/tools/src/cmd/gen/build/build.go
@@ -183,7 +183,8 @@
"*/**.cc",
"*/**.h",
"*/**.inl",
- "*/**.mm"
+ "*/**.mm",
+ "*/**.proto"
]
},
{
@@ -201,7 +202,14 @@
dir, name := path.Split(filepath)
if kind := targetKindFromFilename(name); kind != targetInvalid {
directory := p.AddDirectory(dir)
- p.AddTarget(directory, kind).AddSourceFile(p.AddFile(filepath))
+ target := p.AddTarget(directory, kind)
+ target.AddSourceFile(p.AddFile(filepath))
+
+ if kind == targetProto {
+ noExt, _ := fileutils.SplitExt(filepath)
+ target.AddGeneratedFile(p.AddGeneratedFile(noExt + ".pb.h"))
+ target.AddGeneratedFile(p.AddGeneratedFile(noExt + ".pb.cc"))
+ }
}
}
@@ -222,12 +230,17 @@
// parseFile parses the source file at 'path' represented by 'file'
// As this is run concurrently, it must not modify any shared state (including file)
parseFile := func(path string, file *File) (string, *ParsedFile, error) {
- conditions := []Condition{}
+ if file.IsGenerated {
+ return "", nil, nil
+ }
body, err := os.ReadFile(file.AbsPath())
if err != nil {
return path, nil, err
}
+
+ conditions := []Condition{}
+
out := &ParsedFile{}
for i, line := range strings.Split(string(body), "\n") {
wrapErr := func(err error) error {
@@ -366,6 +379,7 @@
kind TargetKind
}{
{cfg.Lib, targetLib},
+ {cfg.Proto, targetProto},
{cfg.Test, targetTest},
{cfg.TestCmd, targetTestCmd},
{cfg.Bench, targetBench},
diff --git a/tools/src/cmd/gen/build/directory_config.go b/tools/src/cmd/gen/build/directory_config.go
index 0f5ac94..3cd4225 100644
--- a/tools/src/cmd/gen/build/directory_config.go
+++ b/tools/src/cmd/gen/build/directory_config.go
@@ -27,7 +27,7 @@
package build
-// Config for a single target of a directory
+// TargetConfig holds configuration options for a single target of a directory
type TargetConfig struct {
// Override for the output name of this target
OutputName string
@@ -42,12 +42,14 @@
}
}
-// Config for a directory
+// DirectoryConfig holds configuration options for a directory
type DirectoryConfig struct {
// Condition for all targets in the directory
Condition string
// Configuration for the 'lib' target
Lib *TargetConfig
+ // Configuration for the 'proto' target
+ Proto *TargetConfig
// Configuration for the 'test' target
Test *TargetConfig
// Configuration for the 'test_cmd' target
diff --git a/tools/src/cmd/gen/build/file.go b/tools/src/cmd/gen/build/file.go
index b0ad5de..9d74bb4 100644
--- a/tools/src/cmd/gen/build/file.go
+++ b/tools/src/cmd/gen/build/file.go
@@ -38,6 +38,8 @@
// File holds information about a source file
type File struct {
+ // The file is generated from a target
+ IsGenerated bool
// The directory that holds this source file
Directory *Directory
// The target that this source file belongs to
diff --git a/tools/src/cmd/gen/build/project.go b/tools/src/cmd/gen/build/project.go
index 25857f6..96d2bc7 100644
--- a/tools/src/cmd/gen/build/project.go
+++ b/tools/src/cmd/gen/build/project.go
@@ -71,15 +71,36 @@
}
// AddFile gets or creates a File with the given project-relative path
-func (p *Project) AddFile(file string) *File {
- return p.Files.GetOrCreate(file, func() *File {
- dir, name := path.Split(file)
+func (p *Project) AddFile(filepath string) *File {
+ file := p.Files.GetOrCreate(filepath, func() *File {
+ dir, name := path.Split(filepath)
return &File{
Directory: p.Directory(dir),
Name: name,
TransitiveDependencies: NewDependencies(p),
}
})
+ if file.IsGenerated {
+ panic("AddFile() called with path that already exists for generated file")
+ }
+ return file
+}
+
+// AddGeneratedFile gets or creates a generated File with the given project-relative path
+func (p *Project) AddGeneratedFile(filepath string) *File {
+ file := p.Files.GetOrCreate(filepath, func() *File {
+ dir, name := path.Split(filepath)
+ return &File{
+ IsGenerated: true,
+ Directory: p.Directory(dir),
+ Name: name,
+ TransitiveDependencies: NewDependencies(p),
+ }
+ })
+ if !file.IsGenerated {
+ panic("AddGeneratedFile() called with path that already exists for non-generated file")
+ }
+ return file
}
// File returns the File with the given project-relative path
@@ -92,11 +113,12 @@
name := p.TargetName(dir, kind)
return p.Targets.GetOrCreate(name, func() *Target {
t := &Target{
- Name: name,
- Directory: dir,
- Kind: kind,
- SourceFileSet: container.NewSet[string](),
- Dependencies: NewDependencies(p),
+ Name: name,
+ Directory: dir,
+ Kind: kind,
+ SourceFileSet: container.NewSet[string](),
+ GeneratedFileSet: container.NewSet[string](),
+ Dependencies: NewDependencies(p),
}
dir.TargetNames.Add(name)
p.Targets.Add(name, t)
diff --git a/tools/src/cmd/gen/build/target.go b/tools/src/cmd/gen/build/target.go
index 116d8fe..a207b19 100644
--- a/tools/src/cmd/gen/build/target.go
+++ b/tools/src/cmd/gen/build/target.go
@@ -34,7 +34,7 @@
"dawn.googlesource.com/dawn/tools/src/transform"
)
-// Directory holds information about a build target
+// Target holds information about a build target
type Target struct {
// The target's name
Name TargetName
@@ -44,6 +44,8 @@
Directory *Directory
// All project-relative paths of source files that are part of this target
SourceFileSet container.Set[string]
+ // All project-relative paths of files generated by this target
+ GeneratedFileSet container.Set[string]
// Dependencies of this target
Dependencies *Dependencies
// An optional custom output name for the target
@@ -52,12 +54,24 @@
Condition Condition
}
-// AddSourceFile adds the File to the target's source set
+// AddSourceFile adds the File to the target's source file set
func (t *Target) AddSourceFile(f *File) {
+ if f.IsGenerated {
+ panic("attempting to add a generated file to SourceFileSet")
+ }
t.SourceFileSet.Add(f.Path())
f.Target = t
}
+// AddGeneratedFile adds the File to the target's generated file set
+func (t *Target) AddGeneratedFile(f *File) {
+ if !f.IsGenerated {
+ panic("attempting to add a non-generated file to GeneratedFileSet")
+ }
+ t.GeneratedFileSet.Add(f.Path())
+ f.Target = t
+}
+
// SourceFiles returns the sorted list of the target's source files
func (t *Target) SourceFiles() []*File {
out := make([]*File, len(t.SourceFileSet))
@@ -67,12 +81,12 @@
return out
}
-// SourceFiles returns the sorted list of the target's source files that have no build condition
+// UnconditionalSourceFiles returns the sorted list of the target's source files that have no build condition
func (t *Target) UnconditionalSourceFiles() []*File {
return transform.Filter(t.SourceFiles(), func(t *File) bool { return t.Condition == nil })
}
-// A collection of source files and dependencies sharing the same condition
+// TargetConditional is a collection of source files and dependencies sharing the same condition
type TargetConditional struct {
Condition Condition
SourceFiles []*File
@@ -80,7 +94,7 @@
ExternalDependencies []ExternalDependency
}
-// A collection of source files and dependencies sharing the same condition
+// TargetConditionals is a collection of source files and dependencies sharing the same condition
type TargetConditionals []*TargetConditional
// HasSourceFiles returns true if any of the conditionals in l have source files
diff --git a/tools/src/cmd/gen/build/target_kind.go b/tools/src/cmd/gen/build/target_kind.go
index 4556660..9a5977c 100644
--- a/tools/src/cmd/gen/build/target_kind.go
+++ b/tools/src/cmd/gen/build/target_kind.go
@@ -29,6 +29,8 @@
import (
"strings"
+
+ "dawn.googlesource.com/dawn/tools/src/fileutils"
)
// TargetKind is an enumerator of target kinds
@@ -37,6 +39,8 @@
const (
// A library target, used for production code.
targetLib TargetKind = "lib"
+ // A library target generated from a proto file, used for production code.
+ targetProto TargetKind = "proto"
// A library target, used for test binaries.
targetTest TargetKind = "test"
// A library target, used for benchmark binaries.
@@ -58,13 +62,16 @@
// IsLib returns true if the TargetKind is 'lib'
func (k TargetKind) IsLib() bool { return k == targetLib }
+// IsProto returns true if the TargetKind is 'proto'
+func (k TargetKind) IsProto() bool { return k == targetProto }
+
// IsTest returns true if the TargetKind is 'test'
func (k TargetKind) IsTest() bool { return k == targetTest }
// IsBench returns true if the TargetKind is 'bench'
func (k TargetKind) IsBench() bool { return k == targetBench }
-// IsBench returns true if the TargetKind is 'fuzz'
+// IsFuzz returns true if the TargetKind is 'fuzz'
func (k TargetKind) IsFuzz() bool { return k == targetFuzz }
// IsCmd returns true if the TargetKind is 'cmd'
@@ -85,9 +92,10 @@
// IsBenchOrBenchCmd returns true if the TargetKind is 'bench' or 'bench_cmd'
func (k TargetKind) IsBenchOrBenchCmd() bool { return k.IsBench() || k.IsBenchCmd() }
-// All the target kinds
+// AllTargetKinds is a list of all the target kinds
var AllTargetKinds = []TargetKind{
targetLib,
+ targetProto,
targetTest,
targetBench,
targetFuzz,
@@ -99,10 +107,10 @@
// targetKindFromFilename returns the target kind my pattern matching the filename
func targetKindFromFilename(filename string) TargetKind {
- noExt, ext := filename, ""
- if i := strings.LastIndex(filename, "."); i >= 0 {
- noExt = filename[:i]
- ext = filename[i+1:]
+ noExt, ext := fileutils.SplitExt(filename)
+
+ if ext == "proto" {
+ return targetProto
}
if ext != "cc" && ext != "mm" && ext != "h" {
@@ -134,13 +142,13 @@
func isValidDependency(from, to TargetKind) bool {
switch from {
case targetLib, targetCmd:
- return to == targetLib
+ return to == targetLib || to == targetProto
case targetTest, targetTestCmd:
- return to == targetLib || to == targetTest
+ return to == targetLib || to == targetProto || to == targetTest
case targetBench, targetBenchCmd:
- return to == targetLib || to == targetBench
+ return to == targetLib || to == targetProto || to == targetBench
case targetFuzz, targetFuzzCmd:
- return to == targetLib || to == targetFuzz
+ return to == targetLib || to == targetProto || to == targetFuzz
default:
return false
}
diff --git a/tools/src/fileutils/ext.go b/tools/src/fileutils/ext.go
new file mode 100644
index 0000000..e655039
--- /dev/null
+++ b/tools/src/fileutils/ext.go
@@ -0,0 +1,41 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package fileutils
+
+import "strings"
+
+// SplitExt splits the file name at the last '.', returning the no-extension and
+// extension parts.
+func SplitExt(filename string) (noExt, ext string) {
+ noExt, ext = filename, ""
+ if i := strings.LastIndex(filename, "."); i >= 0 {
+ noExt = filename[:i]
+ ext = filename[i+1:]
+ }
+ return
+}