| // 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 ( |
| "fmt" |
| "path" |
| "path/filepath" |
| "strings" |
| |
| "dawn.googlesource.com/dawn/tools/src/cmd/gen/common" |
| "dawn.googlesource.com/dawn/tools/src/container" |
| "dawn.googlesource.com/dawn/tools/src/fileutils" |
| ) |
| |
| // Project holds information about all the directories, targets and source files |
| // that makes up a project. |
| type Project struct { |
| // The command line config |
| cfg *common.Config |
| // The absolute path to the root of the project |
| Root string |
| // A map of project-relative path to File. |
| Files container.Map[string, *File] |
| // A map of project-relative path to Directory. |
| Directories container.Map[string, *Directory] |
| // A map of target name to target. |
| Targets container.Map[TargetName, *Target] |
| // A list of external project dependencies used by the project |
| externals container.Map[ExternalDependencyName, ExternalDependency] |
| // Path to the 'externals.json' file |
| externalsJsonPath string |
| } |
| |
| // NewProject returns a newly initialized Project |
| func NewProject(root string, cfg *common.Config) *Project { |
| return &Project{ |
| cfg: cfg, |
| Root: root, |
| Files: container.NewMap[string, *File](), |
| Directories: container.NewMap[string, *Directory](), |
| Targets: container.NewMap[TargetName, *Target](), |
| externals: container.NewMap[ExternalDependencyName, ExternalDependency](), |
| externalsJsonPath: filepath.Join(fileutils.DawnRoot(), "src", "tint", "externals.json"), |
| } |
| } |
| |
| // AddFile gets or creates a File with the given project-relative path |
| 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 |
| func (p *Project) File(file string) *File { |
| return p.Files[file] |
| } |
| |
| // AddTarget gets or creates a Target with the given Directory and TargetKind |
| func (p *Project) AddTarget(dir *Directory, kind TargetKind) *Target { |
| name := p.TargetName(dir, kind) |
| return p.Targets.GetOrCreate(name, func() *Target { |
| t := &Target{ |
| 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) |
| return t |
| }) |
| } |
| |
| // Target returns the Target with the given Directory and TargetKind |
| func (p *Project) Target(dir *Directory, kind TargetKind) *Target { |
| return p.Targets[p.TargetName(dir, kind)] |
| } |
| |
| // TargetName returns the TargetName of a target in dir with the given kind |
| func (p *Project) TargetName(dir *Directory, kind TargetKind) TargetName { |
| name := TargetName(dir.Path) |
| if kind != targetLib { |
| name += TargetName(fmt.Sprintf(":%v", kind)) |
| } |
| return name |
| } |
| |
| // AddDirectory gets or creates a Directory with the given project-relative path |
| func (p *Project) AddDirectory(path string) *Directory { |
| path = CanonicalizePath(path) |
| return p.Directories.GetOrCreate(path, func() *Directory { |
| split := strings.Split(path, "/") |
| d := &Directory{ |
| Project: p, |
| Name: split[len(split)-1], |
| Path: path, |
| TargetNames: container.NewSet[TargetName](), |
| SubdirectoryNames: container.NewSet[string](), |
| } |
| p.Directories[path] = d |
| |
| if path != "" { |
| d.Parent = p.AddDirectory(strings.Join(split[:len(split)-1], "/")) |
| d.Parent.SubdirectoryNames.Add(d.Name) |
| } |
| return d |
| }) |
| } |
| |
| // Directory returns the Directory with the given project-relative path |
| func (p *Project) Directory(path string) *Directory { |
| return p.Directories[CanonicalizePath(path)] |
| } |
| |
| // CanonicalizePath canonicalizes the given path by changing path delimiters to |
| // '/' and removing any trailing slash |
| func CanonicalizePath(path string) string { |
| return strings.TrimSuffix(filepath.ToSlash(path), "/") |
| } |