| OLD | NEW |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | 1 // Copyright 2017 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package spec | 5 package spec |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bufio" | 8 "bufio" |
| 9 "io/ioutil" | 9 "io/ioutil" |
| 10 "os" | 10 "os" |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 const CommonName = "common" + Suffix | 35 const CommonName = "common" + Suffix |
| 36 | 36 |
| 37 const ( | 37 const ( |
| 38 // DefaultInlineBeginGuard is the default loader InlineBeginGuard value. | 38 // DefaultInlineBeginGuard is the default loader InlineBeginGuard value. |
| 39 DefaultInlineBeginGuard = "[VPYTHON:BEGIN]" | 39 DefaultInlineBeginGuard = "[VPYTHON:BEGIN]" |
| 40 // DefaultInlineEndGuard is the default loader InlineEndGuard value. | 40 // DefaultInlineEndGuard is the default loader InlineEndGuard value. |
| 41 DefaultInlineEndGuard = "[VPYTHON:END]" | 41 DefaultInlineEndGuard = "[VPYTHON:END]" |
| 42 ) | 42 ) |
| 43 | 43 |
| 44 // Load loads an specification file text protobuf from the supplied path. | 44 // Load loads an specification file text protobuf from the supplied path. |
| 45 func Load(path string) (*vpython.Spec, error) { | 45 func Load(path string, spec *vpython.Spec) error { |
| 46 content, err := ioutil.ReadFile(path) | 46 content, err := ioutil.ReadFile(path) |
| 47 if err != nil { | 47 if err != nil { |
| 48 » » return nil, errors.Annotate(err).Reason("failed to load file fro
m: %(path)s"). | 48 » » return errors.Annotate(err).Reason("failed to load file from: %(
path)s"). |
| 49 D("path", path). | 49 D("path", path). |
| 50 Err() | 50 Err() |
| 51 } | 51 } |
| 52 | 52 |
| 53 » spec, err := Parse(string(content)) | 53 » return Parse(string(content), spec) |
| 54 » if err != nil { | |
| 55 » » return nil, errors.Annotate(err).Err() | |
| 56 » } | |
| 57 » return spec, nil | |
| 58 } | 54 } |
| 59 | 55 |
| 60 // Parse loads a specification message from a content string. | 56 // Parse loads a specification message from a content string. |
| 61 func Parse(content string) (*vpython.Spec, error) { | 57 func Parse(content string, spec *vpython.Spec) error { |
| 62 » var spec vpython.Spec | 58 » if err := cproto.UnmarshalTextML(content, spec); err != nil { |
| 63 » if err := cproto.UnmarshalTextML(content, &spec); err != nil { | 59 » » return errors.Annotate(err).Reason("failed to unmarshal vpython.
Spec").Err() |
| 64 » » return nil, errors.Annotate(err).Reason("failed to unmarshal vpy
thon.Spec").Err() | |
| 65 } | 60 } |
| 66 » return &spec, nil | 61 » return nil |
| 67 } | 62 } |
| 68 | 63 |
| 69 // Loader implements the generic ability to load a "vpython" spec file. | 64 // Loader implements the generic ability to load a "vpython" spec file. |
| 70 type Loader struct { | 65 type Loader struct { |
| 71 // InlineBeginGuard is a string that signifies the beginning of an inlin
e | 66 // InlineBeginGuard is a string that signifies the beginning of an inlin
e |
| 72 // specification. If empty, DefaultInlineBeginGuard will be used. | 67 // specification. If empty, DefaultInlineBeginGuard will be used. |
| 73 InlineBeginGuard string | 68 InlineBeginGuard string |
| 74 // InlineEndGuard is a string that signifies the end of an inline | 69 // InlineEndGuard is a string that signifies the end of an inline |
| 75 // specification. If empty, DefaultInlineEndGuard will be used. | 70 // specification. If empty, DefaultInlineEndGuard will be used. |
| 76 InlineEndGuard string | 71 InlineEndGuard string |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 // script's location, looking for a file named CommonName. If it finds one, it | 154 // script's location, looking for a file named CommonName. If it finds one, it |
| 160 // will use that as the specification file. This enables scripts to implicitly | 155 // will use that as the specification file. This enables scripts to implicitly |
| 161 // share an specification. | 156 // share an specification. |
| 162 func (l *Loader) LoadForScript(c context.Context, path string, isModule bool) (*
vpython.Spec, error) { | 157 func (l *Loader) LoadForScript(c context.Context, path string, isModule bool) (*
vpython.Spec, error) { |
| 163 // Partner File: Try loading the spec from an adjacent file. | 158 // Partner File: Try loading the spec from an adjacent file. |
| 164 specPath, err := l.findForScript(path, isModule) | 159 specPath, err := l.findForScript(path, isModule) |
| 165 if err != nil { | 160 if err != nil { |
| 166 return nil, errors.Annotate(err).Reason("failed to scan for file
system spec").Err() | 161 return nil, errors.Annotate(err).Reason("failed to scan for file
system spec").Err() |
| 167 } | 162 } |
| 168 if specPath != "" { | 163 if specPath != "" { |
| 169 » » switch sp, err := Load(specPath); { | 164 » » var spec vpython.Spec |
| 170 » » case err != nil: | 165 » » if err := Load(specPath, &spec); err != nil { |
| 171 » » » return nil, errors.Annotate(err).Reason("failed to load
specification file"). | 166 » » » return nil, err |
| 172 » » » » D("specPath", specPath). | 167 » » } |
| 173 » » » » Err() | |
| 174 | 168 |
| 175 » » case sp != nil: | 169 » » logging.Infof(c, "Loaded specification from: %s", specPath) |
| 176 » » » logging.Infof(c, "Loaded specification from: %s", specPa
th) | 170 » » return &spec, nil |
| 177 » » » return sp, nil | |
| 178 » » } | |
| 179 } | 171 } |
| 180 | 172 |
| 181 // Inline: Try and parse the main script for the spec file. | 173 // Inline: Try and parse the main script for the spec file. |
| 182 mainScript := path | 174 mainScript := path |
| 183 if isModule { | 175 if isModule { |
| 184 // Module. | 176 // Module. |
| 185 mainScript = filepath.Join(mainScript, "__main__.py") | 177 mainScript = filepath.Join(mainScript, "__main__.py") |
| 186 } | 178 } |
| 187 » switch sp, err := l.parseFrom(mainScript); { | 179 » switch spec, err := l.parseFrom(mainScript); { |
| 188 case err != nil: | 180 case err != nil: |
| 189 return nil, errors.Annotate(err).Reason("failed to parse inline
spec from: %(script)s"). | 181 return nil, errors.Annotate(err).Reason("failed to parse inline
spec from: %(script)s"). |
| 190 D("script", mainScript). | 182 D("script", mainScript). |
| 191 Err() | 183 Err() |
| 192 | 184 |
| 193 » case sp != nil: | 185 » case spec != nil: |
| 194 logging.Infof(c, "Loaded inline spec from: %s", mainScript) | 186 logging.Infof(c, "Loaded inline spec from: %s", mainScript) |
| 195 » » return sp, nil | 187 » » return spec, nil |
| 196 } | 188 } |
| 197 | 189 |
| 198 // Common: Try and identify a common specification file. | 190 // Common: Try and identify a common specification file. |
| 199 switch path, err := l.findCommonWalkingFrom(filepath.Dir(mainScript)); { | 191 switch path, err := l.findCommonWalkingFrom(filepath.Dir(mainScript)); { |
| 200 case err != nil: | 192 case err != nil: |
| 201 return nil, err | 193 return nil, err |
| 202 | 194 |
| 203 case path != "": | 195 case path != "": |
| 204 » » spec, err := Load(path) | 196 » » var spec vpython.Spec |
| 205 » » if err != nil { | 197 » » if err := Load(path, &spec); err != nil { |
| 206 return nil, err | 198 return nil, err |
| 207 } | 199 } |
| 208 | 200 |
| 209 logging.Infof(c, "Loaded common spec from: %s", path) | 201 logging.Infof(c, "Loaded common spec from: %s", path) |
| 210 » » return spec, nil | 202 » » return &spec, nil |
| 211 } | 203 } |
| 212 | 204 |
| 213 // Couldn't identify a specification file. | 205 // Couldn't identify a specification file. |
| 214 return nil, nil | 206 return nil, nil |
| 215 } | 207 } |
| 216 | 208 |
| 217 func (l *Loader) findForScript(path string, isModule bool) (string, error) { | 209 func (l *Loader) findForScript(path string, isModule bool) (string, error) { |
| 218 if !isModule { | 210 if !isModule { |
| 219 path += Suffix | 211 path += Suffix |
| 220 if st, err := os.Stat(path); err != nil || st.IsDir() { | 212 if st, err := os.Stat(path); err != nil || st.IsDir() { |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 line = "" | 321 line = "" |
| 330 } | 322 } |
| 331 } else { | 323 } else { |
| 332 line = strings.TrimPrefix(line, prefix) | 324 line = strings.TrimPrefix(line, prefix) |
| 333 } | 325 } |
| 334 content[i] = line | 326 content[i] = line |
| 335 } | 327 } |
| 336 } | 328 } |
| 337 | 329 |
| 338 // Process the resulting file. | 330 // Process the resulting file. |
| 339 » spec, err := Parse(strings.Join(content, "\n")) | 331 » var spec vpython.Spec |
| 340 » if err != nil { | 332 » if err := Parse(strings.Join(content, "\n"), &spec); err != nil { |
| 341 return nil, errors.Annotate(err).Reason("failed to parse spec fi
le from: %(path)s"). | 333 return nil, errors.Annotate(err).Reason("failed to parse spec fi
le from: %(path)s"). |
| 342 D("path", path). | 334 D("path", path). |
| 343 Err() | 335 Err() |
| 344 } | 336 } |
| 345 » return spec, nil | 337 » return &spec, nil |
| 346 } | 338 } |
| 347 | 339 |
| 348 func (l *Loader) findCommonWalkingFrom(startDir string) (string, error) { | 340 func (l *Loader) findCommonWalkingFrom(startDir string) (string, error) { |
| 349 // Walk until we hit root. | 341 // Walk until we hit root. |
| 350 prevDir := "" | 342 prevDir := "" |
| 351 for prevDir != startDir { | 343 for prevDir != startDir { |
| 352 checkPath := filepath.Join(startDir, CommonName) | 344 checkPath := filepath.Join(startDir, CommonName) |
| 353 | 345 |
| 354 switch _, err := os.Stat(checkPath); { | 346 switch _, err := os.Stat(checkPath); { |
| 355 case err == nil: | 347 case err == nil: |
| (...skipping 19 matching lines...) Expand all Loading... |
| 375 } | 367 } |
| 376 } | 368 } |
| 377 | 369 |
| 378 // Walk up a directory. | 370 // Walk up a directory. |
| 379 startDir, prevDir = filepath.Dir(startDir), startDir | 371 startDir, prevDir = filepath.Dir(startDir), startDir |
| 380 } | 372 } |
| 381 | 373 |
| 382 // Couldn't find the file. | 374 // Couldn't find the file. |
| 383 return "", nil | 375 return "", nil |
| 384 } | 376 } |
| OLD | NEW |