blob: a879e317589b58a94a024a83bd99fb1b4cd8bd14 [file] [log] [blame]
// Copyright 2023 The Tint Authors.
//
// 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 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/match"
)
// 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 []projectExternalDependency
}
// projectExternalDependency describes an external dependency of a Project
type projectExternalDependency struct {
// name of the external dependency
name ExternalDependencyName
// include matcher
includePatternMatch match.Test
// condition of the dependency
condition 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](),
}
}
// 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)
return &File{
Directory: p.Directory(dir),
Name: name,
}
})
}
// 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](),
DependencyNames: container.NewSet[TargetName](),
ExternalDependencyMap: container.NewMap[ExternalDependencyName, ExternalDependency](),
}
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), "/")
}