package planr import ( "os" "log" "path" "path/filepath" ) // Standard Directories: // // Planr relies on a standard directory structure to resolve paths to source code // These directories can be overridden via environmental variables // // Directories: // - `.` root directory // - `src` contains source code // - `planr` contains test metadata (also called configuration directory) // - `planr/rubric` contains test case configuration // - `planr/tests` contains source code for test cases // - `planr/build` contains all files written during the build process (ephemeral) // Env overrides const ( ENV_CONFIG_DIR="PLANR_CONFIG_DIR" ENV_SRC_DIR="PLANR_SRC_DIR" ENV_BUILD_DIR="PLANR_BUILD_DIR" ) // Try these search directories var CONFIG_SEARCH_DIRS = [] string { "planr", ".planr", } // Path are relative to the "planr" project config directory const ( DEFAULT_PATH_SRC="../src" DEFAULT_PATH_BUILD="build" DEFAULT_PATH_RUBRIC="rubric" DEFAULT_PATH_TESTS="tests" ) type DirConfig struct { src string config string build string // Config falls back to the config found in the parent directory if the env variable hasn't been overridden pdFallback string } func dieDirAbsent(name, path string) { if !directoryExists(path) { log.Fatalf("Could not find %s directory tried %s", name, path) } } func dirFromEnv(name, env string) *string { if dir, isSet := os.LookupEnv(env); isSet { dieDirAbsent(name, dir) return &dir } return nil } func (c *DirConfig) SetSrc(srcDir string) { dieDirAbsent("src", srcDir) c.src = srcDir } func (c *DirConfig) SetConfig(configDir string) { dieDirAbsent("planr (config)", configDir) c.config = configDir } func (c *DirConfig) SetBuild(buildDir string) { dieDirAbsent("build", buildDir) c.build = buildDir } func (c *DirConfig) SetConfigFromTree(cdir string) { var configDir string found := traverseUp(cdir, func (path string) bool { for _, dir := range CONFIG_SEARCH_DIRS { configDir = filepath.Join(path, dir) if directoryExists(configDir) { return true } } return false }); if found { c.pdFallback = configDir } } func (c DirConfig) Config() string { if c.config != "" { return c.config } if dir := dirFromEnv("config", ENV_CONFIG_DIR); dir != nil { c.config = abs(*dir) return c.config } if c.pdFallback == "" { log.Fatal("Could not find planr directory"); } c.config = abs(c.pdFallback); return c.config } func (c DirConfig) Src() string { if c.src != "" { return c.src } if dir := dirFromEnv("src", ENV_SRC_DIR); dir != nil { c.src = *dir return c.src } // set path relative to config dir := c.Config() return path.Join(dir, DEFAULT_PATH_SRC) } func abs(path string) string { apath, err := filepath.Abs(path) if err != nil { log.Fatalf("Could not find path %s", path) } return apath } func (c DirConfig) Build() string { if c.src != "" { return c.src } if dir := dirFromEnv("build", ENV_BUILD_DIR); dir != nil { c.build = *dir return c.build } dir := c.Config() return path.Join(dir, DEFAULT_PATH_BUILD) } func (c DirConfig) CleanBuild() { build := c.Build() if err := os.RemoveAll(build); err != nil { log.Fatalf("Cannot build directory %v\n", err) } } func (c DirConfig) MkBuild() { build := c.Build() if err := os.Mkdir(build, 0755); err != nil { log.Fatalf("Could not create build directory %v\n", err) } } func (c DirConfig) Rubric() string { rubric := path.Join(c.Config(), DEFAULT_PATH_RUBRIC) dieDirAbsent("rubric", rubric) return rubric } func (c DirConfig) Tests() string { tests := path.Join(c.Config(), DEFAULT_PATH_TESTS) dieDirAbsent("tests", tests) return tests }