blob: 70872aa82ac6295d9ad5c88b9ace75b6ef259096 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package build
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"time"
)
// File represents the path to a file or directory.
type File string
// Path returns a File formed from the joined path segments.
func Path(segments ...string) File {
return File(filepath.Join(segments...))
}
// Join returns a File formed from joining this File with ext.
func (f File) Join(ext ...string) File {
return File(filepath.Join(append([]string{string(f)}, ext...)...))
}
// RelativeTo returns the path of this File relative to base.
func (f File) RelativeTo(base File) string {
rel, err := filepath.Rel(string(base), string(f))
if err != nil {
panic(err)
}
return rel
}
// Absolute returns the absolute path of this File.
func (f File) Absolute() string {
abs, err := filepath.Abs(string(f))
if err != nil {
panic(err)
}
return abs
}
// Exists returns true if this File exists.
func (f File) Exists() bool {
_, err := os.Stat(string(f.Absolute()))
return err == nil
}
// Contains returns true if this directory contains the file f.
func (d File) Contains(f File) bool {
a := d.Absolute()
b := f.Absolute()
if len(b) <= len(a)+1 {
return false
}
return b[:len(a)] == a && b[len(a)] == filepath.Separator
}
// LastModified returns the time the file was last modified.
func (f File) LastModified() time.Time {
s, err := os.Stat(string(f.Absolute()))
if err != nil {
panic(err)
}
return s.ModTime()
}
// Delete deletes the File.
func (f File) Delete() error {
return os.Remove(string(f))
}
// Name returns the name part of the File (without directories).
func (f File) Name() string {
return filepath.Base(string(f))
}
// Dir returns the directory part of the File (without filename).
func (f File) Dir() string {
return filepath.Dir(string(f))
}
// Ext returns the extension of the file, including the '.', or an empty string
// if the file has no extension.
func (f File) Ext() string {
return filepath.Ext(string(f))
}
// ChangeExt returns a new File with the extension changed to ext.
func (f File) ChangeExt(ext string) File {
prev := filepath.Ext(string(f))
noext := f[:len(f)-len(prev)]
return noext + File(ext)
}
// Glob returns a FileSet of all files matching any of the specified patterns.
func (f File) Glob(patterns ...string) FileSet {
fs := FileSet{}
for _, patten := range patterns {
glob, err := filepath.Glob(string(f.Join(patten)))
if err != nil {
panic(err)
}
files := make(FileSet, len(glob))
for i := range glob {
files[i] = File(glob[i])
}
fs = fs.Append(files...)
}
return fs
}
// Matches returns true if the File matches any of the patterns.
func (f File) Matches(patterns ...string) bool {
abs := filepath.Base(f.Absolute())
for _, pattern := range patterns {
matched, err := filepath.Match(pattern, abs)
if err != nil {
panic(err)
}
if matched {
return true
}
}
return false
}
// MkdirAll creates the directory hierarchy to hold this File.
func (f File) MkdirAll() {
abs := f.Absolute()
dir, _ := filepath.Split(abs)
if len(dir) > 0 {
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
panic(err)
}
}
}
// CopyTo copied this File to dst, replacing any existing file at dst.
func (f File) CopyTo(dst File) error {
s, err := os.Open(f.Absolute())
if err != nil {
return fmt.Errorf("Open copy source failed: %v", err)
}
defer s.Close()
fi, err := s.Stat()
if err != nil {
return fmt.Errorf("Stat copy source failed: %v", err)
}
d, err := os.OpenFile(dst.Absolute(), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
if err != nil {
return fmt.Errorf("Create copy target failed: %v", err)
}
defer d.Close()
if _, err = io.Copy(d, s); err != nil {
return fmt.Errorf("Copy file data failed: %v", err)
}
return nil
}
// Exec executes this File with the specified arguments.
func (f File) Exec(env Environment, args ...string) error {
return f.ExecAt(env, "", args...)
}
// ExecAt executes this File with the specified arguments with the working
// directory set to wd.
func (f File) ExecAt(env Environment, wd File, args ...string) error {
logger := env.Logger.Enter("Exec")
var path string
if f.Exists() {
path = string(f)
} else {
var err error
path, err = exec.LookPath(string(f))
if err != nil {
return err
}
}
if env.Verbose {
logger.Infof("%s %v", path, args)
}
cmd := exec.Command(path, args...)
cmd.Dir = string(wd)
buffer := &bytes.Buffer{}
cmd.Stdout = buffer
cmd.Stderr = buffer
err := cmd.Run()
switch {
case err != nil:
logger.Errorf("\n\n%s\n--- %s failed: %v ---", string(buffer.Bytes()), f.Name(), err)
case env.Verbose:
if msg := string(buffer.Bytes()); msg != "" {
logger.Infof("\n%s\n--- %s succeeded ---", string(buffer.Bytes()), f.Name())
} else {
logger.Infof("%s succeeded", f.Name())
}
}
return err
}
// LookPath looks for the file f on the system PATH, returning the absolute
// path to the file if found, otherwise an empty File.
func (f File) LookPath() File {
if path, err := exec.LookPath(string(f)); err == nil {
return File(path)
} else {
return ""
}
}