// ensureBuild invokes the build process on the given repository func ensureBuild(buildStore buildstore.RepoBuildStore, repo *Repo) error { configOpt := config.Options{ Repo: repo.URI(), Subdir: ".", } toolchainExecOpt := ToolchainExecOpt{ExeMethods: "program"} // Config repository if not yet built. exists, err := buildstore.BuildDataExistsForCommit(buildStore, repo.CommitID) if err != nil { return err } if !exists { configCmd := &ConfigCmd{ Options: configOpt, ToolchainExecOpt: toolchainExecOpt, Quiet: true, } if err := configCmd.Execute(nil); err != nil { return err } } // Always re-make. // // TODO(sqs): optimize this makeCmd := &MakeCmd{ Options: configOpt, ToolchainExecOpt: toolchainExecOpt, Quiet: true, } if err := makeCmd.Execute(nil); err != nil { return err } // Always re-import. i := &StoreImportCmd{ ImportOpt: ImportOpt{ Repo: repo.CloneURL, CommitID: repo.CommitID, }, Quiet: true, } if err := i.Execute(nil); err != nil { return err } return nil }
// CreateMakefile creates the makefiles for the source units in c. func CreateMakefile(buildDataDir string, buildStore buildstore.RepoBuildStore, vcsType string, c *config.Tree, opt Options) (*makex.Makefile, error) { var allRules []makex.Rule for i, r := range orderedRuleMakers { name := ruleMakerNames[i] rules, err := r(c, buildDataDir, allRules, opt) if err != nil { return nil, fmt.Errorf("rule maker %s: %s", name, err) } if !opt.NoCache { // When cached builds are enabled, we replace all rules whose source unit // hasn't changed between commits with a rule that copies the build // files stored at the previous commit to the current one. // Check to see if a previous build exists. var prevCommitID string var changedFiles []string if revs, err := listLatestCommitIDs(vcsType); err != nil { log.Printf("Warning: could not list revisions, rebuilding from scratch: %s, %s", revs, err) } else { // Skip HEAD, the first revision in the list. for i := 1; i < len(revs); i++ { if exist, _ := buildstore.BuildDataExistsForCommit(buildStore, revs[i]); !exist { continue } // A build store exists for this commit. Now we need // to get all the changed files between this rev and // the current rev. files, err := filesChangedFromRevToIndex(vcsType, revs[i]) if err != nil { log.Printf("Warning: could not retrieve changed files, rebuilding from scratch: %s %s", files, err) break } changedFiles = files prevCommitID = revs[i] } } if prevCommitID != "" { // Replace rules. for i, rule := range rules { r, ok := rule.(interface { SourceUnit() *unit.SourceUnit }) if !ok { continue } u := r.SourceUnit() if u.ContainsAny(changedFiles) { continue } // The format for p varies based on whether it's prefixed by buildDataDir: // if it is, we simply swap the revision in the file name with the // previous valid revision. If it isn't, we prefix p with // "../[previous-revision]". p := strings.Split(rule.Target(), "/") if len(p) > 2 && strings.Join(p[0:2], "/") == buildDataDir && len(p[1]) == 40 { // HACK: Mercurial and Git both use 40-char hashes. // p is prefixed by "data-dir/vcs-commit-id" p[1] = prevCommitID } else { p = append([]string{"..", prevCommitID}, p...) } rules[i] = &cachedRule{ cachedPath: strings.Join(p, "/"), target: rule.Target(), unit: u, prereqs: rule.Prereqs(), } } } } allRules = append(allRules, rules...) } // Add an "all" rule at the very beginning. allTargets := make([]string, len(allRules)) for i, rule := range allRules { allTargets[i] = rule.Target() } allRule := &makex.BasicRule{TargetFile: "all", PrereqFiles: allTargets} allRules = append([]makex.Rule{allRule}, allRules...) // DELETE_ON_ERROR makes it so that the targets for failed recipes are // deleted. This lets us do "1> $@" to write to the target file without // erroneously satisfying the target if the recipe fails. makex has this // behavior by default and does not heed .DELETE_ON_ERROR. allRules = append(allRules, &makex.BasicRule{TargetFile: ".DELETE_ON_ERROR"}) mf := &makex.Makefile{Rules: allRules} return mf, nil }