blob: 6d5ad5d2d7b761355ffccb9cee36e34881df6354 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 repo
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"mynewt.apache.org/newt/newt/downloader"
"mynewt.apache.org/newt/newt/interfaces"
"mynewt.apache.org/newt/util"
)
const REPO_NAME_LOCAL = "local"
const REPO_DEFAULT_PERMS = 0755
const REPO_FILE_NAME = "repository.yml"
const REPOS_DIR = "repos"
type Repo struct {
name string
downloader downloader.Downloader
localPath string
versreq []interfaces.VersionReqInterface
rdesc *RepoDesc
}
type RepoDesc struct {
name string
vers map[*Version]string
}
func (rd *RepoDesc) MatchVersion(searchVers *Version) (string, *Version, bool) {
for vers, curBranch := range rd.vers {
if vers.CompareVersions(vers, searchVers) == 0 {
return curBranch, vers, true
}
}
return "", nil, false
}
func (rd *RepoDesc) Match(r *Repo) (string, *Version, bool) {
for vers, branch := range rd.vers {
if vers.SatisfiesVersion(r.VersionRequirements()) {
log.Printf("[DEBUG] Found matching version %s for repo %s",
vers.String(), r.Name())
if vers.Stability() != VERSION_STABILITY_NONE {
searchVers, err := LoadVersion(branch)
if err != nil {
return "", nil, false
}
var ok bool
branch, vers, ok = rd.MatchVersion(searchVers)
if !ok {
return "", nil, false
}
}
return branch, vers, true
}
}
return "", nil, false
}
func (rd *RepoDesc) SatisfiesVersion(vers *Version, versReqs []interfaces.VersionReqInterface) bool {
var err error
versMatches := []interfaces.VersionReqInterface{}
for _, versReq := range versReqs {
versMatch := &VersionMatch{}
versMatch.compareType = versReq.CompareType()
if versReq.Version().Stability() != VERSION_STABILITY_NONE {
// Look up this item in the RepoDescription, and get a version
searchVers := versReq.Version().(*Version)
branch, _, ok := rd.MatchVersion(searchVers)
if !ok {
return false
}
versMatch.Vers, err = LoadVersion(branch)
if err != nil {
return false
}
} else {
versMatch.Vers = versReq.Version().(*Version)
}
versMatches = append(versMatches, versMatch)
}
return vers.SatisfiesVersion(versMatches)
}
func (rd *RepoDesc) Init(name string, versBranchMap map[string]string) error {
rd.name = name
rd.vers = map[*Version]string{}
for versStr, branch := range versBranchMap {
log.Printf("[DEBUG] Printing version %s for remote repo %s", versStr, name)
vers, err := LoadVersion(versStr)
if err != nil {
return err
}
// store branch->version mapping
rd.vers[vers] = branch
}
return nil
}
func (rd *RepoDesc) String() string {
name := rd.name + "@"
for k, v := range rd.vers {
name += fmt.Sprintf("%s=%s", k.String(), v)
name += "#"
}
return name
}
func NewRepoDesc(name string, versBranchMap map[string]string) (*RepoDesc, error) {
rd := &RepoDesc{}
if err := rd.Init(name, versBranchMap); err != nil {
return nil, err
}
return rd, nil
}
func (r *Repo) GetRepoDesc() (*RepoDesc, error) {
if r.rdesc == nil {
return nil, util.NewNewtError(fmt.Sprintf(
"Repository description for %s not yet initailized. Must "+
"download it first. ", r.Name()))
} else {
return r.rdesc, nil
}
}
func (r *Repo) Name() string {
return r.name
}
func (r *Repo) Path() string {
return r.localPath
}
func (r *Repo) IsLocal() bool {
return r.name == REPO_NAME_LOCAL
}
func (r *Repo) VersionRequirements() []interfaces.VersionReqInterface {
return r.versreq
}
func (r *Repo) VersionRequirementsString() string {
str := ""
for _, vreq := range r.versreq {
str += vreq.String()
}
return str
}
func (r *Repo) repoFilePath() string {
return interfaces.GetProject().Path() + "/" + REPOS_DIR + "/" +
".configs/" + r.name + "/"
}
func (r *Repo) Install(force bool) (*Version, error) {
// Copy the git repo into /repos/, error'ing out if the repo already exists
if util.NodeExist(r.Path()) {
if force {
if err := os.RemoveAll(r.Path()); err != nil {
return nil, util.NewNewtError(err.Error())
}
} else {
return nil, util.NewNewtError(fmt.Sprintf("Repository %s already "+
"exists in local tree, cannot install. Provide -f to override.", r.Path()))
}
}
branchName, vers, found := r.rdesc.Match(r)
if !found {
return nil, util.NewNewtError(fmt.Sprintf("No repository matching description %s found",
r.rdesc.String()))
}
dl := r.downloader
// Download the git repo, returns the git repo, checked out to that branch
tmpdir, err := dl.DownloadRepo(branchName)
if err != nil {
return nil, util.NewNewtError(fmt.Sprintf("Error download repository %s, : %s",
r.Name(), err.Error()))
}
// Copy the Git repo into the the desired local path of the repo
if err := util.CopyDir(tmpdir, r.Path()); err != nil {
// Cleanup any directory that might have been created if we error out
// here.
os.RemoveAll(r.Path())
return nil, err
}
return vers, nil
}
func (r *Repo) UpdateDesc() ([]*Repo, error) {
var err error
if err = r.DownloadDesc(); err != nil {
return []*Repo{}, err
}
_, err = r.ReadDesc()
if err != nil {
return []*Repo{}, err
}
return []*Repo{}, nil
}
// Download the repository description.
func (r *Repo) DownloadDesc() error {
dl := r.downloader
util.StatusMessage(util.VERBOSITY_DEFAULT, fmt.Sprintf("Downloading "+
"repository description for %s...", r.Name()))
// Configuration path
cpath := r.repoFilePath()
if util.NodeNotExist(cpath) {
if err := os.MkdirAll(cpath, REPO_DEFAULT_PERMS); err != nil {
return util.NewNewtError(err.Error())
}
}
dl.SetBranch("master")
if err := dl.FetchFile("repository.yml",
cpath+"/"+"repository.yml"); err != nil {
util.StatusMessage(util.VERBOSITY_DEFAULT, " failed\n")
return err
}
util.StatusMessage(util.VERBOSITY_DEFAULT, " success!\n")
return nil
}
func (r *Repo) ReadDesc() (*RepoDesc, error) {
if util.NodeNotExist(r.repoFilePath() + REPO_FILE_NAME) {
return nil,
util.NewNewtError("No configuration exists for repository " + r.name)
}
v, err := util.ReadConfig(r.repoFilePath(),
strings.TrimSuffix(REPO_FILE_NAME, ".yml"))
if err != nil {
return nil, err
}
name := v.GetString("repo.name")
versMap := v.GetStringMapString("repo.versions")
rdesc, err := NewRepoDesc(name, versMap)
if err != nil {
return nil, err
}
r.rdesc = rdesc
return rdesc, nil
}
func (r *Repo) Init(repoName string, rversreq string, d downloader.Downloader) error {
var err error
r.name = repoName
r.downloader = d
r.versreq, err = LoadVersionMatches(rversreq)
if err != nil {
return err
}
path := interfaces.GetProject().Path()
if r.name == REPO_NAME_LOCAL {
r.localPath = filepath.Clean(path)
} else {
r.localPath = filepath.Clean(path + "/" + REPOS_DIR + "/" + r.name)
}
return nil
}
func NewRepo(repoName string, rversreq string, d downloader.Downloader) (*Repo, error) {
r := &Repo{}
if err := r.Init(repoName, rversreq, d); err != nil {
return nil, err
}
return r, nil
}
func NewLocalRepo() (*Repo, error) {
r := &Repo{}
if err := r.Init(REPO_NAME_LOCAL, "", nil); err != nil {
return nil, err
}
return r, nil
}