|  | // 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 files generated by this target | 
|  | GeneratedFileSet 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.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)) | 
|  | for i, name := range t.SourceFileSet.List() { | 
|  | out[i] = t.Directory.Project.Files[name] | 
|  | } | 
|  | return out | 
|  | } | 
|  |  | 
|  | // 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 }) | 
|  | } | 
|  |  | 
|  | // 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] | 
|  | if file.Condition != nil { | 
|  | c := m.GetOrCreate(file.Condition.String(), func() *TargetConditional { | 
|  | return &TargetConditional{Condition: file.Condition} | 
|  | }) | 
|  | c.SourceFiles = append(c.SourceFiles, file) | 
|  | } | 
|  | } | 
|  | for name := range t.Dependencies.internal { | 
|  | dep := t.Directory.Project.Targets[name] | 
|  | if dep.Condition != nil { | 
|  | c := m.GetOrCreate(dep.Condition.String(), func() *TargetConditional { | 
|  | return &TargetConditional{Condition: dep.Condition} | 
|  | }) | 
|  | c.InternalDependencies = append(c.InternalDependencies, dep) | 
|  | } | 
|  | } | 
|  | for name := range t.Dependencies.external { | 
|  | dep := t.Directory.Project.externals[name] | 
|  | if dep.Condition != nil { | 
|  | c := m.GetOrCreate(dep.Condition.String(), func() *TargetConditional { | 
|  | return &TargetConditional{Condition: dep.Condition} | 
|  | }) | 
|  | 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()) | 
|  | } |