| // 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 build | |
| import ( | |
| "sort" | |
| "dawn.googlesource.com/dawn/tools/src/container" | |
| "dawn.googlesource.com/dawn/tools/src/transform" | |
| ) | |
| // Target holds information about a build target | |
| type Target struct { | |
| // The target's name | |
| Name TargetName | |
| // The kind of target | |
| Kind TargetKind | |
| // The directory holding the target | |
| Directory *Directory | |
| // All project-relative paths of source files that are part of this target | |
| SourceFileSet container.Set[string] | |
| // All project-relative paths of template files that are part of this target | |
| TemplateFileSet container.Set[string] | |
| // All project-relative paths of files generated by this target (Protobuf) | |
| GeneratedProtobufSources container.Set[string] | |
| // All project-relative paths of source files that are generated | |
| GeneratedSourcePaths container.Set[string] | |
| // Dependencies of this target | |
| Dependencies *Dependencies | |
| // An optional custom output name for the target | |
| OutputName string | |
| // An optional condition for building this target | |
| Condition Condition | |
| } | |
| // AddSourceFile adds the File to the target's source file set | |
| func (t *Target) AddSourceFile(f *File) { | |
| if f.IsGeneratedProtobufSource { | |
| panic("attempting to add a generated file to SourceFileSet") | |
| } | |
| t.SourceFileSet.Add(f.Path()) | |
| f.Target = t | |
| } | |
| // AddTemplateFile adds the File to the target's template file set | |
| func (t *Target) AddTemplateFile(f *File) { | |
| if f.IsGeneratedProtobufSource { | |
| panic("attempting to add a generated file to TemplateFileSet") | |
| } | |
| t.TemplateFileSet.Add(f.Path()) | |
| f.Target = t | |
| } | |
| // AddGeneratedProtobufSource adds the File to the target's generated protobuf source set | |
| func (t *Target) AddGeneratedProtobufSource(f *File) { | |
| if !f.IsGeneratedProtobufSource { | |
| panic("attempting to add a non-generated file to GeneratedProtobufSources") | |
| } | |
| t.GeneratedProtobufSources.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)) | |
| for i, name := range t.SourceFileSet.List() { | |
| out[i] = t.Directory.Project.Files[name] | |
| } | |
| return out | |
| } | |
| // TemplateFiles returns the sorted list of the target's template files | |
| func (t *Target) TemplateFiles() []*File { | |
| out := make([]*File, len(t.TemplateFileSet)) | |
| for i, name := range t.TemplateFileSet.List() { | |
| out[i] = t.Directory.Project.Files[name] | |
| } | |
| return out | |
| } | |
| // IsGeneratedSource returns true if the file at 'path' is a generated source file | |
| func (t *Target) IsGeneratedSource(path string) bool { | |
| return t.GeneratedSourcePaths.Contains(path) | |
| } | |
| // 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(f *File) bool { | |
| return len(f.Condition.AssumeTrue(t.Condition)) == 0 | |
| }) | |
| } | |
| // UnconditionalInternalDependencies returns the sorted list of the target's internal dependencies | |
| // that have no build condition (simplified by the target's condition). | |
| func (t *Target) UnconditionalInternalDependencies() []*Target { | |
| return transform.Filter(t.Dependencies.Internal(), func(d *Target) bool { | |
| return len(d.Condition.AssumeTrue(t.Condition)) == 0 | |
| }) | |
| } | |
| // UnconditionalExternalDependencies returns the sorted list of the target's external dependencies | |
| // that have no build condition (simplified by the target's condition). | |
| func (t *Target) UnconditionalExternalDependencies() []ExternalDependency { | |
| return transform.Filter(t.Dependencies.External(), func(e ExternalDependency) bool { | |
| return len(e.Condition.AssumeTrue(t.Condition)) == 0 | |
| }) | |
| } | |
| // TargetConditional is a collection of source files and dependencies sharing the same condition | |
| type TargetConditional struct { | |
| Condition Condition | |
| SourceFiles []*File | |
| InternalDependencies []*Target | |
| ExternalDependencies []ExternalDependency | |
| } | |
| // 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 | |
| func (l TargetConditionals) HasSourceFiles() bool { | |
| for _, c := range l { | |
| if len(c.SourceFiles) > 0 { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // HasDependencies returns true if any of the conditionals in l have internal or external dependencies | |
| func (l TargetConditionals) HasDependencies() bool { | |
| for _, c := range l { | |
| if len(c.InternalDependencies) > 0 || len(c.ExternalDependencies) > 0 { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // Conditionals returns a sorted list of TargetConditional, which are grouped by condition | |
| func (t *Target) Conditionals() TargetConditionals { | |
| m := container.NewMap[string, *TargetConditional]() | |
| for name := range t.SourceFileSet { | |
| file := t.Directory.Project.Files[name] | |
| cond := file.Condition.AssumeTrue(t.Condition) | |
| if len(cond) > 0 { | |
| c := m.GetOrCreate(cond.String(), func() *TargetConditional { | |
| return &TargetConditional{Condition: cond} | |
| }) | |
| c.SourceFiles = append(c.SourceFiles, file) | |
| } | |
| } | |
| for name := range t.Dependencies.internal { | |
| dep := t.Directory.Project.Targets[name] | |
| cond := dep.Condition.AssumeTrue(t.Condition) | |
| if len(cond) > 0 { | |
| c := m.GetOrCreate(cond.String(), func() *TargetConditional { | |
| return &TargetConditional{Condition: cond} | |
| }) | |
| c.InternalDependencies = append(c.InternalDependencies, dep) | |
| } | |
| } | |
| for name := range t.Dependencies.external { | |
| dep := t.Directory.Project.externals[name] | |
| cond := dep.Condition.AssumeTrue(t.Condition) | |
| if len(cond) > 0 { | |
| c := m.GetOrCreate(cond.String(), func() *TargetConditional { | |
| return &TargetConditional{Condition: cond} | |
| }) | |
| c.ExternalDependencies = append(c.ExternalDependencies, dep) | |
| } | |
| } | |
| for _, c := range m { | |
| sort.Slice(c.SourceFiles, func(a, b int) bool { return c.SourceFiles[a].Name < c.SourceFiles[b].Name }) | |
| sort.Slice(c.InternalDependencies, func(a, b int) bool { return c.InternalDependencies[a].Name < c.InternalDependencies[b].Name }) | |
| sort.Slice(c.ExternalDependencies, func(a, b int) bool { return c.ExternalDependencies[a].Name < c.ExternalDependencies[b].Name }) | |
| } | |
| return TargetConditionals(m.Values()) | |
| } |