Skip to content

Commit

Permalink
format comments, walk_derivation_graph, more progress
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmcd committed Sep 6, 2021
1 parent 85f022d commit beb202c
Show file tree
Hide file tree
Showing 20 changed files with 591 additions and 172 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054
github.com/containerd/console v1.0.0
github.com/creack/pty v1.1.11
github.com/davecgh/go-spew v1.1.1
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23
github.com/go-git/go-git/v5 v5.3.0
github.com/google/go-cmp v0.5.5 // indirect
Expand Down
8 changes: 6 additions & 2 deletions notes/28-pkg-reorg.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ type ExecModuleInput struct {
}

type ExecModuleOutput struct {
Output []Derivation
AllDerivations []Derivation
Output []project.Derivation
AllDerivations []project.Derivation
}

type BuildInput struct {
Expand All @@ -119,6 +119,10 @@ type Bramble interface {
NewProject(wd string) (*Project, error)
NewStore(bramblePath string) (*Store, error)
ExecModule(input ExecModuleInput) (ExecModuleOutput, error)
// replace a dependency hash value with another hash value
ReplaceDerivationHash(drv project.Derivation, hash string) error
// Convert derivation arguments into a built or unbuilt derivation
NewDerivation(args DerivationArgs) (drv build.Derivation, err error)
Build(input BuildInput) (BuildOutput, error)
}
```
Expand Down
11 changes: 6 additions & 5 deletions pkg/assert/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ var (
assertErr error
)

// LoadAssertModule loads the assert module.
// It is concurrency-safe and idempotent.
// LoadAssertModule loads the assert module. It is concurrency-safe and
// idempotent.
func LoadAssertModule() (starlark.StringDict, error) {
once.Do(func() {
predeclared := starlark.StringDict{
Expand All @@ -112,8 +112,8 @@ func LoadAssertModule() (starlark.StringDict, error) {
return assert, assertErr
}

// catch(f) evaluates f() and returns its evaluation error message
// if it failed or None if it succeeded.
// catch(f) evaluates f() and returns its evaluation error message if it failed
// or None if it succeeded.
func catch(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var fn starlark.Callable
if err := starlark.UnpackArgs("catch", args, kwargs, "fn", &fn); err != nil {
Expand All @@ -125,7 +125,8 @@ func catch(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kw
return starlark.None, nil
}

// matches(pattern, str) reports whether string str matches the regular expression pattern.
// matches(pattern, str) reports whether string str matches the regular
// expression pattern.
func matches(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var pattern, str string
if err := starlark.UnpackArgs("matches", args, kwargs, "pattern", &pattern, "str", &str); err != nil {
Expand Down
56 changes: 56 additions & 0 deletions pkg/bramble2/bramble_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package bramble

import (
"path/filepath"
"testing"

"github.com/davecgh/go-spew/spew"
build "github.com/maxmcd/bramble/pkg/bramblebuild"
project "github.com/maxmcd/bramble/pkg/brambleproject"
"github.com/stretchr/testify/require"
)

func TestBasic(t *testing.T) {
projectLocation, err := filepath.Abs("../brambleproject/testdata/project")
require.NoError(t, err)
gotOutput, err := project.ExecModule(project.ExecModuleInput{
Command: "build",
Arguments: []string{":chain"},
ProjectInput: project.ProjectInput{
WorkingDirectory: projectLocation,
ProjectLocation: projectLocation,
ModuleName: "testproject",
},
})
require.NoError(t, err)
a := gotOutput.AllDerivations[0]

store, err := build.NewStore("")
require.NoError(t, err)

exists, drv, err := store.NewDerivation2(build.NewDerivationOptions{
Args: a.Args,
Builder: a.Builder,
Env: a.Env,
// InputDerivations: , TODO
Name: a.Name,
Outputs: a.Outputs,
Platform: a.Platform,
Sources: build.SourceFiles{
ProjectLocation: projectLocation,
Location: a.Sources.Location,
Files: a.Sources.Files,
},
})
require.NoError(t, err)
spew.Dump(exists, drv)

// Arrange the allDerivations so that you're starting with dependencies
// without dependents. Crawl the grap, when you run NewDerivation above take
// the hash and replace it in all child dependents. Then build that new
// derivation. Then hand those derivations to a build function and build the
// unbuilt derivations while patching up the graph again.
//
// Maybe think about a good generalization for building the graph and then
// patching it up, would be great to stop implementing that everywhere.
}
8 changes: 4 additions & 4 deletions pkg/bramblebuild/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,8 @@ func (s *Store) archiveAndScanOutputDirectory(ctx context.Context, tarOutput, ha
}
}()

// replace all the bramble store path prefixes with a known fixed value
// also write this byte stream out as a tar to unpack later as the final output
// replace all the bramble store path prefixes with a known fixed value also
// write this byte stream out as a tar to unpack later as the final output
go func() {
new := BramblePrefixOfRecord
_, matches, err := textreplace.ReplaceStringsPrefix(
Expand All @@ -453,8 +453,8 @@ func (s *Store) archiveAndScanOutputDirectory(ctx context.Context, tarOutput, ha
}()

// swap out references in the output to itself with null bytes so that
// builds with a different randomly named build directory will still
// match the hash of this one
// builds with a different randomly named build directory will still match
// the hash of this one
go func() {
if _, err := textreplace.ReplaceBytes(
pipeReader2, hashOutput,
Expand Down
37 changes: 19 additions & 18 deletions pkg/bramblebuild/derivation.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"strings"
"sync"

"github.com/maxmcd/bramble/pkg/dstruct"
ds "github.com/maxmcd/bramble/pkg/data_structures"
"github.com/maxmcd/bramble/pkg/fileutil"
"github.com/maxmcd/bramble/pkg/hasher"
"github.com/maxmcd/dag"
Expand All @@ -28,10 +28,10 @@ var (
UnbuiltDerivationOutputTemplate = "{{ %s:%s }}"
BuiltDerivationOutputTemplate = "{{ %s }}"

// UnbuiltTemplateStringRegexp is the regular expression that matches template strings
// in our derivations. I assume the ".*" parts won't run away too much because
// of the earlier match on "{{ [0-9a-z]{32}" but might be worth further
// investigation.
// UnbuiltTemplateStringRegexp is the regular expression that matches
// template strings in our derivations. I assume the ".*" parts won't run
// away too much because of the earlier match on "{{ [0-9a-z]{32}" but might
// be worth further investigation.
//
// TODO: should we limit the content of the derivation name? non-latin would
// be good for users but bad for filesystems. What's a sensible limiation
Expand All @@ -55,11 +55,12 @@ type Derivation struct {
Builder string
// Env are environment variables set during the build
Env map[string]string
// InputDerivations are derivations that are using as imports to this build, outputs
// dependencies are tracked in the outputs
// InputDerivations are derivations that are using as imports to this build,
// outputs dependencies are tracked in the outputs
InputDerivations DerivationOutputs
// Name is the name of the derivation
Name string

// Outputs are build outputs, a derivation can have many outputs, the
// default output is called "out". Multiple outputs are useful when your
// build process can produce multiple artifacts, but building them as a
Expand Down Expand Up @@ -282,8 +283,8 @@ func (drv *Derivation) Filename() (filename string) {
return
}

func (drv *Derivation) BuildDependencyGraph() (graph *dstruct.AcyclicGraph, err error) {
graph = dstruct.NewAcyclicGraph()
func (drv *Derivation) BuildDependencyGraph() (graph *ds.AcyclicGraph, err error) {
graph = ds.NewAcyclicGraph()
var processInputDerivations func(drv *Derivation, do DerivationOutput) error
processInputDerivations = func(drv *Derivation, do DerivationOutput) error {
graph.Add(do)
Expand All @@ -305,9 +306,9 @@ func (drv *Derivation) BuildDependencyGraph() (graph *dstruct.AcyclicGraph, err
// If there are multiple build outputs we'll need to create a fake root and
// connect all of the build outputs to our fake root.
if len(dos) > 1 {
graph.Add(dstruct.FakeDAGRoot)
graph.Add(ds.FakeDAGRoot)
for _, do := range dos {
graph.Connect(dag.BasicEdge(dstruct.FakeDAGRoot, do))
graph.Connect(dag.BasicEdge(ds.FakeDAGRoot, do))
}
}
for _, do := range dos {
Expand All @@ -320,8 +321,8 @@ func (drv *Derivation) BuildDependencyGraph() (graph *dstruct.AcyclicGraph, err

// RuntimeDependencyGraph graphs the full dependency graph needed at runtime for
// all outputs. Includes all immediate dependencies and their dependencies
func (drv *Derivation) RuntimeDependencyGraph() (graph *dstruct.AcyclicGraph, err error) {
graph = dstruct.NewAcyclicGraph()
func (drv *Derivation) RuntimeDependencyGraph() (graph *ds.AcyclicGraph, err error) {
graph = ds.NewAcyclicGraph()
noOutput := errors.New("outputs missing on derivation when searching for runtime dependencies")
if drv.MissingOutput() {
return nil, noOutput
Expand Down Expand Up @@ -439,8 +440,8 @@ func (drv *Derivation) CopyWithOutputValuesReplaced() (copy *Derivation, err err
}

// DerivationOutput tracks the build outputs. Outputs are not included in the
// Derivation hash. The path tracks the output location in the bramble store
// and Dependencies tracks the bramble outputs that are runtime dependencies.
// Derivation hash. The path tracks the output location in the bramble store and
// Dependencies tracks the bramble outputs that are runtime dependencies.
type Output struct {
Path string
Dependencies []string
Expand All @@ -453,9 +454,9 @@ func (o Output) Empty() bool {
return false
}

// DerivationOutput is one of the derivation inputs. Path is the location of
// the derivation, output is the name of the specific output this derivation
// uses for the build
// DerivationOutput is one of the derivation inputs. Path is the location of the
// derivation, output is the name of the specific output this derivation uses
// for the build
type DerivationOutput struct {
Filename string
OutputName string
Expand Down
6 changes: 3 additions & 3 deletions pkg/bramblebuild/derivation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strings"
"testing"

"github.com/maxmcd/bramble/pkg/dstruct"
ds "github.com/maxmcd/bramble/pkg/data_structures"
"github.com/maxmcd/dag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -51,7 +51,7 @@ func TestDerivationOutputChange(t *testing.T) {
// We pretend to build
counter := 1
graph.Walk(func(v dag.Vertex) error {
if v == dstruct.FakeDAGRoot {
if v == ds.FakeDAGRoot {
return nil
}
do := v.(DerivationOutput)
Expand Down Expand Up @@ -83,7 +83,7 @@ func TestDerivationOutputChange(t *testing.T) {

newTemplateName := drv.TemplateString()
for _, edge := range graph.EdgesTo(v) {
if edge.Source() == dstruct.FakeDAGRoot {
if edge.Source() == ds.FakeDAGRoot {
continue
}
childDO := edge.Source().(DerivationOutput)
Expand Down
99 changes: 99 additions & 0 deletions pkg/bramblebuild/new_derivation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package bramblebuild

import (
"os"
"path/filepath"
"sort"

"github.com/maxmcd/bramble/pkg/fileutil"
"github.com/maxmcd/bramble/pkg/hasher"
"github.com/maxmcd/bramble/pkg/reptar"
)

type NewDerivationOptions struct {
Args []string
Builder string
Env map[string]string
InputDerivations DerivationOutputs
Name string
Outputs []string
Platform string
Sources SourceFiles
}

type SourceFiles struct {
ProjectLocation string
Location string
Files []string
}

func (s *Store) hashAndStoreSources(drv *Derivation, sources SourceFiles) (err error) {
// TODO: could extend reptar to handle hasing the files before moving
// them to a tempdir
tmpDir, err := s.TempDir()
if err != nil {
return
}

absDir, err := filepath.Abs(sources.Location)
if err != nil {
return
}
// get absolute paths for all sources
for i, src := range sources.Files {
sources.Files[i] = filepath.Join(sources.ProjectLocation, src)
}

prefix := fileutil.CommonFilepathPrefix(append(sources.Files, absDir))
relBramblefileLocation, err := filepath.Rel(prefix, absDir)
if err != nil {
return
}

if err = fileutil.CopyFilesByPath(prefix, sources.Files, tmpDir); err != nil {
return
}
// sometimes the location the derivation runs from is not present
// in the structure of the copied source files. ensure that we add it
runLocation := filepath.Join(tmpDir, relBramblefileLocation)
if err = os.MkdirAll(runLocation, 0755); err != nil {
return
}
hshr := hasher.NewHasher()
if err = reptar.Reptar(tmpDir, hshr); err != nil {
return
}
storeLocation := s.JoinStorePath(hshr.String())
if fileutil.PathExists(storeLocation) {
if err = os.RemoveAll(tmpDir); err != nil {
return
}
} else {
if err = os.Rename(tmpDir, storeLocation); err != nil {
return
}
}
drv.BuildContextSource = hshr.String()
drv.BuildContextRelativePath = relBramblefileLocation
drv.SourcePaths = append(drv.SourcePaths, hshr.String())
sort.Strings(drv.SourcePaths)
return
}

func (s *Store) NewDerivation2(options NewDerivationOptions) (exists bool, drv *Derivation, err error) {
drv = s.NewDerivation()
if err = s.hashAndStoreSources(drv, options.Sources); err != nil {
return
}
drv.store = s
drv.Args = options.Args
drv.Builder = options.Builder
drv.Name = options.Name
drv.Env = options.Env
drv.InputDerivations = options.InputDerivations
drv.Platform = options.Platform
drv.OutputNames = options.Outputs // TODO: Validate, and others

exists, err = drv.PopulateOutputsFromStore()
return exists, drv, err
}
Loading

0 comments on commit beb202c

Please sign in to comment.