Skip to content

Commit b80280b

Browse files
author
Christian Weichel
committed
[layout] Make build-time filesystem layout explicit
Prior to this change the build-time filesystem layout was created implicitly from the package names. This method breaks for nested workspaces where package names differ depending on their context. This makes built-time layout explicit by introducing a "layout" section in the package description. If no explicit layout is set, leeway will fall back to its prior behaviour and use the dependency's filesystem safe name. This mechanism is implemented during linking s.t. the build-time layout stays stable across nested workspaces. `leeway describe` has been updated accordingly to show the layout. `leeway vet` finds layout path clashes if two dependencies occupy the same path at build-time.
1 parent b8ec99f commit b80280b

File tree

7 files changed

+91
-16
lines changed

7 files changed

+91
-16
lines changed

cmd/describe.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ type packageDescription struct {
159159
Manifest map[string]string `json:"manifest" yaml:"manifest"`
160160
ArgDeps []string `json:"argdeps,omitempty" yaml:"argdeps,omitempty"`
161161
Dependencies []packageMetadataDescription `json:"dependencies,omitempty" yaml:"dependencies,omitempty"`
162+
Layout map[string]string `json:"layout,omitempty" yaml:"layout,omitempty"`
162163
Config configDescription `json:"config,omitempty" yaml:"config,omitempty"`
163164
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
164165
Definition string `json:"definition,omitempty"`
@@ -181,11 +182,17 @@ func newPackageDesription(pkg *leeway.Package) packageDescription {
181182
}
182183
sort.Slice(deps, func(i, j int) bool { return deps[i].FullName < deps[j].FullName })
183184

185+
layout := make(map[string]string)
186+
for _, dep := range pkg.GetDependencies() {
187+
layout[dep.FullName()] = pkg.BuildLayoutLocation(dep)
188+
}
189+
184190
return packageDescription{
185191
Metadata: newMetadataDescription(pkg),
186192
Type: string(pkg.Type),
187193
ArgDeps: pkg.ArgumentDependencies,
188194
Dependencies: deps,
195+
Layout: layout,
189196
Env: pkg.Environment,
190197
Manifest: manifest,
191198
Config: newConfigDescription(pkg.Type, pkg.Config),
@@ -249,6 +256,10 @@ Version Relevant Arguments:
249256
Dependencies:
250257
{{- range $k, $v := .Dependencies }}
251258
{{"\t"}}{{ $v.FullName -}}{{"\t"}}{{ $v.Version -}}
259+
{{ end }}
260+
Layout:
261+
{{- range $k, $v := .Layout }}
262+
{{"\t"}}{{ $k -}}{{"\t"}}{{ $v -}}
252263
{{ end -}}
253264
{{ end }}
254265
{{ if .Manifest -}}

pkg/leeway/build.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ func (p *Package) buildYarn(buildctx *buildContext, wd, result string) (err erro
619619
return PkgNotBuiltErr{deppkg}
620620
}
621621

622-
tgt := deppkg.FilesystemSafeName()
622+
tgt := p.BuildLayoutLocation(deppkg)
623623
if cfg.Packaging == YarnOfflineMirror {
624624
fn := fmt.Sprintf("%s.tar.gz", tgt)
625625
commands = append(commands, []string{"cp", builtpkg, filepath.Join("_mirror", fn)})
@@ -803,7 +803,7 @@ func (p *Package) buildGo(buildctx *buildContext, wd, result string) (err error)
803803
return PkgNotBuiltErr{dep}
804804
}
805805

806-
tgt := filepath.Join("_deps", dep.FilesystemSafeName())
806+
tgt := filepath.Join("_deps", p.BuildLayoutLocation(dep))
807807
commands = append(commands, [][]string{
808808
{"mkdir", tgt},
809809
{"tar", "xfz", builtpkg, "-C", tgt},
@@ -869,7 +869,7 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (err er
869869
return PkgNotBuiltErr{dep}
870870
}
871871

872-
tgt := dep.FilesystemSafeName()
872+
tgt := p.BuildLayoutLocation(dep)
873873
commands = append(commands, [][]string{
874874
{"mkdir", tgt},
875875
{"tar", "xfz", fn, "-C", tgt},
@@ -941,7 +941,7 @@ func (p *Package) buildGeneric(buildctx *buildContext, wd, result string) (err e
941941
return PkgNotBuiltErr{dep}
942942
}
943943

944-
tgt := dep.FilesystemSafeName()
944+
tgt := p.BuildLayoutLocation(dep)
945945
commands = append(commands, [][]string{
946946
{"mkdir", tgt},
947947
{"tar", "xfz", fn, "-C", tgt},

pkg/leeway/package.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,15 @@ func (n PackageNotFoundErr) Error() string {
111111
}
112112

113113
type packageInternal struct {
114-
Name string `yaml:"name"`
115-
Type PackageType `yaml:"type"`
116-
Sources []string `yaml:"srcs"`
117-
Dependencies []string `yaml:"deps"`
118-
ArgumentDependencies []string `yaml:"argdeps"`
119-
Environment []string `yaml:"env"`
120-
Ephemeral bool `yaml:"ephemeral"`
121-
PreparationCommands [][]string `yaml:"prep"`
114+
Name string `yaml:"name"`
115+
Type PackageType `yaml:"type"`
116+
Sources []string `yaml:"srcs"`
117+
Dependencies []string `yaml:"deps"`
118+
Layout map[string]string `yaml:"layout"`
119+
ArgumentDependencies []string `yaml:"argdeps"`
120+
Environment []string `yaml:"env"`
121+
Ephemeral bool `yaml:"ephemeral"`
122+
PreparationCommands [][]string `yaml:"prep"`
122123
}
123124

124125
// Package is a single buildable artifact within a component
@@ -134,6 +135,7 @@ type Package struct {
134135
Definition []byte `yaml:"-"`
135136

136137
dependencies []*Package
138+
layout map[*Package]string
137139
originalSources []string
138140
fullNameOverride string
139141
}
@@ -146,12 +148,19 @@ func (p *Package) link(idx map[string]*Package) error {
146148
}
147149

148150
p.dependencies = make([]*Package, len(p.Dependencies))
151+
p.layout = make(map[*Package]string)
149152
for i, dep := range p.Dependencies {
150-
var ok bool
151-
p.dependencies[i], ok = idx[dep]
153+
deppkg, ok := idx[dep]
152154
if !ok {
153155
return PackageNotFoundErr{dep}
154156
}
157+
p.dependencies[i] = deppkg
158+
159+
// if the user hasn't specified a layout, tie it down at this point
160+
p.layout[deppkg], ok = p.Layout[dep]
161+
if !ok {
162+
p.layout[deppkg] = deppkg.FilesystemSafeName()
163+
}
155164
}
156165
return nil
157166
}
@@ -239,6 +248,19 @@ func (p *Package) GetTransitiveDependencies() []*Package {
239248
return res
240249
}
241250

251+
// BuildLayoutLocation returns the filesystem path a dependency is expected at during the build.
252+
// This path will always be relative. If the provided package is not a depedency of this package,
253+
// we'll still return a valid path.
254+
func (p *Package) BuildLayoutLocation(dependency *Package) (loc string) {
255+
var ok bool
256+
loc, ok = p.layout[dependency]
257+
if ok {
258+
return loc
259+
}
260+
261+
return p.FilesystemSafeName()
262+
}
263+
242264
// UnmarshalYAML unmarshals the package definition
243265
func (p *Package) UnmarshalYAML(unmarshal func(interface{}) error) error {
244266
var tpe packageInternal

pkg/leeway/workspace.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,18 @@ func loadComponent(ctx context.Context, workspace *Workspace, path string, args
489489

490490
pkg.Dependencies[idx] = comp.Name + dep
491491
}
492+
// make all layout entries full qualified
493+
if pkg.Layout == nil {
494+
pkg.Layout = make(map[string]string)
495+
}
496+
for dep, loc := range pkg.Layout {
497+
if !strings.HasPrefix(dep, ":") {
498+
continue
499+
}
500+
501+
delete(pkg.Layout, dep)
502+
pkg.Layout[comp.Name+dep] = loc
503+
}
492504

493505
// apply variant config
494506
if vnt := pkg.C.W.SelectedVariant; vnt != nil {

pkg/vet/docker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func checkDockerCopyFromPackage(pkg *leeway.Package) ([]Finding, error) {
7272
// we've found something that looks like a path - check if we have a dependency that could satisfy it
7373
var satisfied bool
7474
for _, dep := range pkg.GetDependencies() {
75-
if dep.FilesystemSafeName() == pth {
75+
if pkg.BuildLayoutLocation(dep) == pth {
7676
satisfied = true
7777
break
7878
}

pkg/vet/generic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func checkArgsReferingToPackage(pkg *leeway.Package) ([]Finding, error) {
3232
// we've found something that looks like a path - check if we have a dependency that could satisfy it
3333
var satisfied bool
3434
for _, dep := range pkg.GetDependencies() {
35-
if dep.FilesystemSafeName() == pth {
35+
if pkg.BuildLayoutLocation(dep) == pth {
3636
satisfied = true
3737
break
3838
}

pkg/vet/packages.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package vet
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/typefox/leeway/pkg/leeway"
7+
)
8+
9+
func init() {
10+
register(PackageCheck("build-layout", "validates the build layout of all packages", "", checkBuildLayout))
11+
}
12+
13+
func checkBuildLayout(pkg *leeway.Package) (findings []Finding, err error) {
14+
layoutIdx := make(map[string]string)
15+
for dep, loc := range pkg.Layout {
16+
otherdep, taken := layoutIdx[loc]
17+
if !taken {
18+
layoutIdx[loc] = dep
19+
continue
20+
}
21+
22+
findings = append(findings, Finding{
23+
Description: fmt.Sprintf("build-time location %v is used by %v and %v", loc, dep, otherdep),
24+
Component: pkg.C,
25+
Error: true,
26+
Package: pkg,
27+
})
28+
}
29+
return
30+
}

0 commit comments

Comments
 (0)