for k,_ in pairs(t) do
if not lookup[k] then
if not e then
- e = err.new("table %s contains unexpected key %q",
- name, tostring(k))
+ e = err.new("unexpected key %q in %s",
+ tostring(k), name)
else
- e:append("table %s contains unexpected key %q",
- name, tostring(k))
+ e = err.new("unexpected key %q in %s",
+ tostring(k), name)
end
end
end
e2-build-numbers e2-cf e2-help
LOCALLUALIBS= digest.lua e2build.lua e2tool.lua environment.lua \
- policy.lua scm.lua licence.lua chroot.lua project.lua
+ policy.lua scm.lua licence.lua chroot.lua project.lua \
+ source.lua sl.lua
LOCALTOOLS = $(LOCALLUATOOLS)
.PHONY: all install uninstall local install-local doc install-doc
local err = require("err")
local scm = require("scm")
local chroot = require("chroot")
+local source = require("source")
local function e2_fetch_source(arg)
local rc, re = e2lib.init()
local e = err.new() -- no message yet, append the summary later on
-- fetch
- for _, s in pairs(info.sources) do
- local has_wc = scm.has_working_copy(info, s.name)
- local wc_avail = scm.working_copy_available(info, s.name)
- if opts.fetch and sel[s.name] then
+ for sourcename, _ in pairs(source.sources) do
+ local has_wc = scm.has_working_copy(info, sourcename)
+ local wc_avail = scm.working_copy_available(info, sourcename)
+ if opts.fetch and sel[sourcename] then
if wc_avail then
e2lib.logf(1,
- "working copy for %s is already available", s.name)
+ "working copy for %s is already available", sourcename)
else
- e2lib.logf(1, "fetching working copy for source %s", s.name)
- local rc, re = scm.fetch_source(info, s.name)
+ e2lib.logf(1, "fetching working copy for source %s", sourcename)
+ local rc, re = scm.fetch_source(info, sourcename)
if not rc then
e:cat(re)
end
end
-- update
- for _, s in pairs(info.sources) do
- local has_wc = scm.has_working_copy(info, s.name)
- local wc_avail = scm.working_copy_available(info, s.name)
- if opts.update and has_wc and sel[s.name] then
+ for sourcename, _ in pairs(source.sources) do
+ local has_wc = scm.has_working_copy(info, sourcename)
+ local wc_avail = scm.working_copy_available(info, sourcename)
+ if opts.update and has_wc and sel[sourcename] then
if not wc_avail then
- e2lib.logf(1, "working copy for %s is not available", s.name)
+ e2lib.logf(1, "working copy for %s is not available", sourcename)
else
- e2lib.logf(1, "updating working copy for %s", s.name)
- local rc, re = scm.update(info, s.name)
+ e2lib.logf(1, "updating working copy for %s", sourcename)
+ local rc, re = scm.update(info, sourcename)
if not rc then
e:cat(re)
end
local sel = {} -- selected sources
if #arguments > 0 then
- for _, x in pairs(arguments) do
- if info.sources[x] and not opts.result then
- e2lib.logf(3, "is regarded as source: %s", x)
- sel[x] = x
- elseif info.results[x] and opts.result then
- e2lib.logf(3, "is regarded as result: %s", x)
- local res = info.results[x]
- for _, s in ipairs(res.sources) do
- sel[s] = s
+ for _, srcresname in pairs(arguments) do
+ if source.sources[srcresname] and not opts.result then
+ e2lib.logf(3, "is regarded as source: %s", srcresname)
+ sel[srcresname] = true
+ elseif info.results[srcresname] and opts.result then
+ e2lib.logf(3, "is regarded as result: %s", srcresname)
+ local res = info.results[srcresname]
+ for _, sourcename in ipairs(res.sources) do
+ sel[sourcename] = true
end
elseif opts.result then
- error(err.new("is not a result: %s", x))
+ error(err.new("is not a result: %s", srcresname))
else
- error(err.new("is not a source: %s", x))
+ error(err.new("is not a source: %s", srcresname))
end
end
elseif opts["all"] then
-- select all sources
- for s,src in pairs(info.sources) do
- sel[s] = s
+ for sourcename, _ in pairs(source.sources) do
+ sel[sourcename] = true
end
end
-- select all sources by scm type
- for s, src in pairs(info.sources) do
- if select_type[src.type] then
- sel[s] = s
+ for sourcename, src in pairs(source.sources) do
+ if select_type[src:get_type()] then
+ sel[sourcename] = true
end
end
- for _, s in pairs(sel) do
- e2lib.logf(2, "selecting source: %s" , s)
- local src = info.sources[s]
+ for sourcename, _ in pairs(sel) do
+ e2lib.logf(2, "selecting source: %s" , sourcename)
+ local src = source.sources[sourcename]
if not src then
- e:append("selecting invalid source: %s", s)
+ e:append("selecting invalid source: %s", sourcename)
end
end
if e:getcount() > 0 then
local scm = require("scm")
local chroot = require("chroot")
local project = require("project")
+local source = require("source")
local function e2_ls_project(arg)
local rc, re = e2lib.init()
local sources = {}
if opts.all then
- for s, _ in pairs(info.sources) do
- table.insert(sources, s)
+ for sourcename, _ in pairs(source.sources) do
+ table.insert(sources, sourcename)
end
else
local yet = {}
for _, r in pairs(results) do
- for _, s in ipairs(info.results[r].sources) do
- if not yet[s] then
- table.insert(sources, s)
- yet[s] = true
+ for _, sourcename in ipairs(info.results[r].sources) do
+ if not yet[sourcename] then
+ table.insert(sources, sourcename)
+ yet[sourcename] = true
end
end
end
local s2 = " "
p1(s1, s2, "src")
local len = #sources
- for _, s in pairs(sources) do
- local src = info.sources[s]
+ for _, sourcename in pairs(sources) do
len = len - 1
if len == 0 then
s2 = " "
else
s2 = "|"
end
- p2(s1, s2, src.name)
- local t, re = scm.display(info, src.name)
+ p2(s1, s2, sourcename)
+ local t, re = source.sources[sourcename]:display()
if not t then
error(re)
end
local url = require("url")
local chroot = require("chroot")
local project = require("project")
+local source = require("source")
-- Build function table, see end of file for details.
local e2tool_ftab = {}
e:append("source attribute:")
e:cat(re)
else
- for i,s in ipairs(res.sources) do
- if not info.sources[s] then
- e:append("source does not exist: %s", s)
+ for _,sourcename in ipairs(res.sources) do
+ if not source.sources[sourcename] then
+ e:append("source does not exist: %s", sourcename)
end
end
end
return pathname:gsub("/", ".")
end
---- Load all source configs. Creates and populates the info.sources dictionary.
--- @param info Info table.
--- @return True on success, false on error.
--- @return Error object on failure.
-local function load_source_configs(info)
- local rc, re, e
- local sources, list, path, types
-
- e = err.new("error loading source configuration")
- info.sources = {}
- sources, re = gather_source_paths(info)
- if not sources then
- return false, e:cat(re)
- end
-
- for _,src in ipairs(sources) do
- path = e2tool.sourceconfig(src, info.root)
- types = { "e2source", }
- rc, re = e2tool.verify_src_res_pathname_valid_chars(src)
- if not rc then
- e:append("invalid source file name: %s", src)
- e:cat(re)
- return false, e
- end
-
- list, re = load_user_config2(info, path, types)
- if not list then
- return false, e:cat(re)
- end
-
- local name
- for _,item in ipairs(list) do
- name = item.data.name
- item.data.directory = src
- if not name and #list == 1 then
- e2lib.warnf("WDEFAULT", "`name' attribute missing in source config.")
- e2lib.warnf("WDEFAULT", " Defaulting to directory name")
- item.data.name = e2tool.src_res_path_to_name(src)
- name = item.data.name
- end
-
- if not name then
- return false, e:append("`name' attribute missing in source config")
- end
-
- rc, re = e2tool.verify_src_res_name_valid_chars(name)
- if not rc then
- e:append("invalid source name: %s", name)
- e:cat(re)
- return false, e
- end
-
- if info.sources[name] then
- return false, e:append("duplicate source: %s", name)
- end
-
- info.sources[name] = item.data
- end
- end
- return true
-end
-
--- Get project-relative directory for a result.
-- Returns the relative path to the resultdir and optionally a name and prefix
-- (e.g. prefix/res/name).
return true
end
---- check source.
-local function check_source(info, sourcename)
- local src = info.sources[sourcename]
- local rc, e, re
- if not src then
- e = err.new("no source by that name: %s", sourcename)
- return false, e
- end
- e = err.new("in source: %s", sourcename)
- if not src.type then
- e2lib.warnf("WDEFAULT", "in source %s", sourcename)
- e2lib.warnf("WDEFAULT", " type attribute defaults to `files'")
- src.type = "files"
- end
- rc, re = scm.validate_source(info, sourcename)
- if not rc then
- return false, re
- end
- return true
-end
-
---- check sources.
-local function check_sources(info)
- local rc, re
- local e = err.new("Error while checking sources")
- for n,s in pairs(info.sources) do
- rc, re = check_source(info, n)
- if not rc then
- e:cat(re)
- end
- end
- if e:getcount() > 1 then
- return false, e
- end
- return true
-end
-
--- Checks project information for consistancy.
-- @param info Info table.
-- @return True on success, false on error.
local function check_project_info(info)
local rc, re, e
e = err.new("error in project configuration")
- rc, re = check_sources(info)
- if not rc then
- return false, e:cat(re)
- end
+
rc, re = check_results(info)
if not rc then
return false, e:cat(re)
end
end
- info.sources = {}
-
-- read environment configuration
info.env = {} -- global and result specfic env (deprecated)
info.env_files = {} -- a list of environment files
end
-- sources
- rc, re = load_source_configs(info)
+ rc, re = source.load_source_configs(info)
if not rc then
return false, e:cat(re)
end
end
end
- --e2tool.add_source_results(info)
-
-- provide a sorted list of results
info.results_sorted = {}
for r,res in pairs(info.results) do
end
table.sort(info.results_sorted)
- -- provided sorted list of sources
- info.sources_sorted = {}
- for s,src in pairs(info.sources) do
- table.insert(info.sources_sorted, s)
- end
- table.sort(info.sources_sorted)
-
rc, re = policy.init(info)
if not rc then
return false, e:cat(re)
hash.hash_line(hc, r.name)
- for _,s in ipairs(r.sources) do
- local src = info.sources[s]
- local source_set = r.build_mode.source_set()
- local rc, re, sourceid = scm.sourceid(info, s, source_set)
- if not rc then
+ for _,sourcename in ipairs(r.sources) do
+ local src, sourceid, sourceset
+
+ src = source.sources[sourcename]
+ sourceset = r.build_mode.source_set()
+ sourceid, re = src:sourceid(sourceset)
+ if not sourceid then
return false, e:cat(re)
end
- hash.hash_line(hc, s) -- source name
+
+ hash.hash_line(hc, sourcename) -- source name
hash.hash_line(hc, sourceid) -- sourceid
end
for _,d in ipairs(r.depends) do
-- @param resultname string: name of a result
-- @return table: environment variables valid for the result
function e2tool.env_by_result(info, resultname)
+ assert(type(info) == "table")
+ assert(type(resultname) == "string" and #resultname > 0)
+
+ local src
local res = info.results[resultname]
local env = environment.new()
env:merge(info.global_env, false)
- for _, s in ipairs(res.sources) do
- env:merge(info.sources[s]._env, true)
+ for _, sourcename in ipairs(res.sources) do
+ src = source.sources[sourcename]
+ env:merge(src:get_env(), true)
end
env:merge(res._env, true)
return env
]]
local scm = {}
-local e2lib = require("e2lib")
-local environment = require("environment")
+package.loaded["scm"] = scm
local err = require("err")
-local licence = require("licence")
local strict = require("strict")
+local source = require("source")
-- scm modules
local scms = {}
end
local function func(info, sourcename, ...)
- local src = info.sources[sourcename]
+ assert(info)
+ assert(sourcename)
+
+ local typ
local rc, re, e
+
e = err.new("calling scm operation failed")
- if not scms[src.type] then
- return false, e:append("no such source type: %s", src.type)
+
+ typ = source.sources[sourcename]:get_type()
+ if not scms[typ] then
+ return false, e:append("no such source type: %s", tostring(typ))
end
- local f = scms[src.type][name]
+ local f = scms[typ][name]
if not f then
- e:append("%s() is not implemented for source type: %s",
- name, src.type)
+ e:append("%s() is not implemented for source type: %s", name, typ)
return false, e
end
return f(info, sourcename, ...)
return true
end
---- apply default values where possible and a source configuration is
--- incomplete
--- @param info the info table
--- @param sourcename the source name
--- @return bool
--- @return an error object on failure
-local function source_apply_default_licences(info, sourcename)
- local e = err.new("applying default licences failed.")
- local src = info.sources[ sourcename ]
-
- if not src.licences and src.licence then
- e2lib.warnf("WDEPRECATED", "in source %s:", src.name)
- e2lib.warnf("WDEPRECATED",
- " licence attribute is deprecated. Replace by licences.")
- src.licences = src.licence
- end
- if src.licences == nil then
- e2lib.warnf("WDEFAULT", "in source %s:", src.name)
- e2lib.warnf("WDEFAULT",
- " licences attribute missing. Defaulting to empty list.")
- src.licences = {}
- elseif type(src.licences) == "string" then
- e2lib.warnf("WDEPRECATED", "in source %s:", src.name)
- e2lib.warnf("WDEPRECATED",
- " licences attribute is not in table format. Converting.")
- src.licences = { src.licences }
- end
-
- if type(src.licences) ~= "table" then
- e:append("licences attribute is of invalid type")
- return false, e
- end
-
- for i, s in pairs(src.licences) do
- if type(i) ~= "number" or type(s) ~= "string" then
- e:append("licences attribute is not a list of strings")
- return false, e
- end
- end
- for _,l in ipairs(src.licences) do
- if not licence.licences[l] then
- e:append("unknown licence: %s", l)
- return false, e
- end
- end
- return true
-end
-
---- validate generic source configuration, usable by SCM plugins
--- @param info the info table
--- @param sourcename the source name
--- @return bool
--- @return an error object on failure
-function scm.generic_source_validate(info, sourcename)
- local src = info.sources[sourcename]
- local rc, re
- local e
- if not src then
- return false, err.new("invalid source: %s", sourcename)
- end
- e = err.new("in source %s:", sourcename)
- rc, re = source_apply_default_licences(info, sourcename)
- if not rc then
- return false, e:cat(re)
- end
- if not src.type then
- e:append("source has no `type' attribute")
- end
- if src.env and type(src.env) ~= "table" then
- e:append("source has invalid `env' attribute")
- else
- if not src.env then
- e2lib.warnf("WDEFAULT",
- "source has no `env' attribute. Defaulting to empty dictionary")
- src.env = {}
- end
- src._env = environment.new()
- for k,v in pairs(src.env) do
- if type(k) ~= "string" then
- e:append("in `env' dictionary: key is not a string: %s", tostring(k))
- elseif type(v) ~= "string" then
- e:append("in `env' dictionary: value is not a string: %s", tostring(v))
- else
- src._env:set(k, v)
- end
- end
- end
- if e:getcount() > 1 then
- return false, e
- end
- return true, nil
-end
-
---- apply default values where possible
--- @param info the info table
--- @param sourcename the source name
--- @return bool
--- @return an error object on failure
-function scm.generic_source_default_working(info, sourcename)
- local src
-
- src = info.sources[sourcename]
-
- if not src.working then
- src.working = e2lib.join("in", sourcename)
-
- e2lib.warnf("WDEFAULT", "in source %s:", sourcename)
- e2lib.warnf("WDEFAULT", " `working' attribute defaults to '%s'.",
- src.working)
- end
-
- return true
-end
-
--- do some consistency checks required before using sources
-- @param info
-- @param sourcename string: source name
return true, nil
end
-scm.register_interface("sourceid")
-scm.register_interface("validate_source")
scm.register_interface("toresult")
scm.register_interface("prepare_source")
scm.register_interface("fetch_source")
scm.register_interface("update")
scm.register_interface("check_workingcopy")
scm.register_interface("working_copy_available")
-scm.register_interface("display")
scm.register_interface("has_working_copy")
return strict.lock(scm)
--- /dev/null
+--- Universal string list. Handy for storing result-, licence-, source names.
+-- @module local.sl
+
+-- Copyright (C) 2014 emlix GmbH, see file AUTHORS
+--
+-- This file is part of e2factory, the emlix embedded build system.
+-- For more information see http://www.e2factory.org
+--
+-- e2factory is a registered trademark of emlix GmbH.
+--
+-- e2factory is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU General Public License as published by the
+-- Free Software Foundation, either version 3 of the License, or (at your
+-- option) any later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+-- more details.
+
+local sl = {}
+local class = require("class")
+local err = require("err")
+local e2lib = require("e2lib")
+local strict = require("strict")
+
+-- ----------------------------------------------------------------------------
+-- There is plenty of optimization potential here, however the string lists
+-- are usually very small: 0 - 100 entries. Don't waste time.
+-- ----------------------------------------------------------------------------
+
+--- Class "sl" for keeping string lists.
+-- Trying to use string list with anything but strings throws an exception.
+sl.sl = class("sl")
+
+--- Initialize string list [sl:new()]. Merge and unique can't be set both.
+-- @param merge Whether entries are to be merged, defaults to false (boolean).
+-- @param unique Whether inserting duplicate entries raises errors,
+-- defaults to false (boolean).
+function sl.sl:initialize(merge, unique)
+ assert(merge == nil or type(merge) == "boolean")
+ assert(unique == nil or type(unique) == "boolean")
+ assert(not (merge and unique))
+
+ self._merge = merge or false
+ self._unique = unique or false
+ self._list = {}
+end
+
+--- Insert an entry into the string list.
+-- @param entry The entry.
+-- @return True on success, false when the entry is not unique.
+function sl.sl:insert(entry)
+ assert(type(entry) == "string")
+
+ if self._merge then
+ if self:lookup(entry) then
+ return true
+ end
+ elseif self._unique then
+ if self:lookup(entry) then
+ return false
+ end
+ end
+ table.insert(self._list, entry)
+ return true
+end
+
+--- Remove *all* matching entries from the string list.
+-- @param entry The entry.
+-- @return True when one or more entries were removed, false otherwise.
+function sl.sl:remove(entry)
+ assert(type(entry) == "string")
+ local changed, i
+
+ changed = false
+ i = 1
+ while self._list[i] do
+ if self._list[i] == entry then
+ table.remove(self._list, i)
+ changed = true
+ else
+ i = i+1
+ end
+ end
+
+ return changed
+end
+
+--- Check whether entry is in string list.
+-- @param entry The search entry.
+-- @return True if entry is in the string list, false otherwise.
+function sl.sl:lookup(entry)
+ assert(type(entry) == "string")
+
+ for k, v in ipairs(self._list) do
+ if v == entry then
+ return true
+ end
+ end
+
+ return false
+end
+
+--- Return the number of entries in the string list.
+-- @return Number of entries, 0 if empty.
+function sl.sl:size()
+ return #self._list
+end
+
+--- Iterate through the string list in insertion order.
+-- @return Iterator function.
+function sl.sl:iter_inserted()
+
+ local i = 0
+
+ return function()
+ i = i + 1
+ return self._list[i]
+ end
+end
+
+--- Iterate through the string list in alphabetical order.
+-- @return Iterator function.
+function sl.sl:iter_sorted()
+ local t = {}
+ local i = 0
+
+ for _,v in ipairs(self._list) do
+ table.insert(t, v)
+ end
+ table.sort(t)
+
+ return function()
+ i = i + 1
+ return t[i]
+ end
+end
+
+--- Create in independent string list copy.
+-- @return New string list object.
+function sl.sl:copy()
+ local c = sl.sl:new(self._merge, self._unique)
+ for e in self:iter_inserted() do
+ assert(c:insert(e))
+ end
+ assert(self:size() == c:size())
+ return c
+end
+
+--- Concatenate the string list in alphabetical order.
+-- @param sep Separator, defaults to empty string.
+-- @return Concatenated string.
+function sl.sl:concat_sorted(sep)
+ assert(sep == nil or type(sep) == "string")
+ local first = true
+ local cat = ""
+ sep = sep or ""
+
+ for e in self:iter_sorted() do
+ if first then
+ cat = e
+ first = false
+ else
+ cat = cat..sep..e
+ end
+ end
+
+ return cat
+end
+
+--- Return string list entries as an array, in insertion order.
+-- @return Array in insertion order.
+function sl.sl:totable_inserted()
+ local t = {}
+ for _,v in ipairs(self._list) do
+ table.insert(t, v)
+ end
+ return t
+end
+
+--- Return string list entries as an array, in insertion order.
+-- @return Array in insertion order.
+function sl.sl:totable_sorted()
+ return table.sort(self:totable_inserted())
+end
+
+--[[
+local function selftest()
+ local s1 = sl.sl:new()
+
+ assert(s1:size() == 0)
+ assert(s1.class.name == "sl")
+
+ s1:insert("ccc")
+ s1:insert("bbb")
+ s1:insert("aaa")
+ s1:insert("aaa")
+
+ assert(s1:size() == 4)
+
+ local c = 1
+ for entry in s1:iter_inserted() do
+ assert(c <= s1:size() and c > 0)
+ if c == 1 then assert(entry == "ccc") end
+ if c == 2 then assert(entry == "bbb") end
+ if c == 3 then assert(entry == "aaa") end
+ if c == 4 then assert(entry == "aaa") end
+ c = c+1
+ end
+
+ assert(s1:lookup("foo") == false)
+ assert(s1:lookup("bbb") == true)
+
+ s1:insert("xxx")
+ assert(s1:size() == 5)
+ c = 1
+ for entry in s1:iter_sorted() do
+ assert(c <= s1:size() and c > 0)
+ if c == 1 then assert(entry == "aaa") end
+ if c == 2 then assert(entry == "aaa") end
+ if c == 3 then assert(entry == "bbb") end
+ if c == 4 then assert(entry == "ccc") end
+ if c == 5 then assert(entry == "xxx") end
+ c = c+1
+ end
+
+ assert(s1:remove("doesnotexist") == false)
+ assert(s1:remove("aaa") == true)
+ assert(s1:size() == 3)
+ c = 1
+ for entry in s1:iter_sorted() do
+ assert(c <= s1:size() and c > 0)
+ --e2lib.logf(1, "entry=%s", entry)
+ if c == 1 then assert(entry == "bbb") end
+ if c == 2 then assert(entry == "ccc") end
+ if c == 3 then assert(entry == "xxx") end
+ c = c+1
+ end
+
+ assert(s1:concat_sorted() == "bbbcccxxx")
+ assert(s1:concat_sorted("y") == "bbbycccyxxx")
+
+ local s2 = sl.sl:new(false, true)
+
+ c = false
+ for _,v in ipairs({"bbb", "aaa", "xxx", "foo", "bla", "bar", "xxx"}) do
+ if not s2:insert(v) then
+ c = true
+ assert(v == "xxx")
+ end
+ end
+ assert(c == true)
+
+ local s3 = sl.sl:new(true, false)
+
+ for _,v in ipairs({"bbb", "aaa", "xxx", "foo", "bar", "bar", "xxx", "y"}) do
+ assert(s3:insert(v) == true)
+ end
+ assert(s3:size() == 6)
+
+ local s4 = sl.sl:new()
+ s4:insert("")
+ s4:insert("")
+ s4:insert("")
+
+ assert(s4:concat_sorted() == "")
+ assert(s4:concat_sorted("x") == "xx")
+end
+
+selftest()
+--]]
+
+return strict.lock(sl)
+
+-- vim:sw=4:sts=4:et:
--- /dev/null
+--- Source base class. Implements the base source class and config loader.
+-- @module local.source
+
+-- Copyright (C) 2007-2014 emlix GmbH, see file AUTHORS
+--
+-- This file is part of e2factory, the emlix embedded build system.
+-- For more information see http://www.e2factory.org
+--
+-- e2factory is a registered trademark of emlix GmbH.
+--
+-- e2factory is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU General Public License as published by the
+-- Free Software Foundation, either version 3 of the License, or (at your
+-- option) any later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+-- more details.
+
+local source = {}
+package.loaded["source"] = source
+
+local cache = require("cache")
+local class = require("class")
+local e2lib = require("e2lib")
+local e2tool = require("e2tool")
+local environment = require("environment")
+local err = require("err")
+local licence = require("licence")
+local sl = require("sl")
+local strict = require("strict")
+
+--- Dictionary indexed by source type, derived source class.
+local source_types = {}
+
+--- Source base class.
+source.basic_source = class("basic_source")
+
+--- Source base constructor. Assert error on invalid input.
+-- @param rawsrc Source config dict containing at least "name" and "type"
+-- attributes.
+function source.basic_source:initialize(rawsrc)
+ assert(type(rawsrc) == "table")
+ assert(type(rawsrc.name) == "string" and rawsrc.name ~= "")
+ assert(type(rawsrc.type) == "string" and rawsrc.type ~= "")
+
+ self._name = rawsrc.name
+ self._type = rawsrc.type
+ self._licences = false
+ self._env = false
+end
+
+--- Get name.
+-- @return Name of source (Ex: group.result).
+function source.basic_source:get_name()
+ assert(type(self._name) == "string")
+ return self._name
+end
+
+--- Get name as directory path.
+-- @return Path of source (ex: group/result).
+function source.basic_source:get_name_as_path()
+ assert(type(self._name) == "string")
+ local p = e2tool.src_res_name_to_path(self._name)
+ assert(type(p) == "string")
+ return p
+end
+
+--- Get type of source.
+-- @return Type of source (ex: "files", "git", ...)
+function source.basic_source:get_type()
+ return self._type
+end
+
+--- Set licence array.
+-- @param licences String list of licence names (sl).
+function source.basic_source:set_licences(licences)
+ assert(type(licences) == "table" and licences.class.name == "sl")
+ self._licences = licences:copy()
+end
+
+--- Get licence array. Must be set before calling get_licences(). Note that
+-- this returns all licences used in a source. Some sources may have more
+-- detailed licensing information which can be accessed by other means.
+-- @return String list of licence names (sl).
+function source.basic_source:get_licences()
+ assert(type(self._licences) == "table")
+ return self._licences:copy()
+end
+
+--- Set env object.
+-- @param env Env object.
+function source.basic_source:set_env(env)
+ assert(type(env) == "table")
+ self._env = env
+end
+
+--- Get env object. Must be set before calling get_env().
+-- @return Env object.
+function source.basic_source:get_env()
+ assert(type(self._env) == "table")
+ return self._env
+end
+
+--- Abstract sourceid method. Every child class must overwrite this
+-- method with an implementation. Calling this method throws an error.
+-- @param sourceset Source set (ex: "tag", "branch", ...)
+-- @return Sourceid string (usually a hash value) or false on error.
+-- @return Error object on failure.
+function source.basic_source:sourceid(sourceset)
+ error(err.new("called sourceid() of source base class, type %s name %s",
+ self._type, self._name))
+end
+
+--- Abstract display method. Every child class must overwrite this
+-- method with an implementation. Calling this method throws an error.
+-- @return Array of strings containing free form information about source.
+function source.basic_source:display()
+ error(err.new("called display() of source base class, type %s name %s",
+ self._type, self._name))
+end
+
+--- Dictionary holding all source objects indexed by their name.
+source.sources = {}
+--- Array holding all source objects in alphabetical order.
+source.sources_sorted = {}
+
+--- Gather source paths.
+-- @param info Info table.
+-- @param basedir Nil or directory from where to start scanning for more
+-- sources. Only for recursion.
+-- @param sources Nil or table of source paths. Only for recursion.
+-- @return Table with source paths, or false on error.
+-- @return Error object on failure.
+local function gather_source_paths(info, basedir, sources)
+ local rc, re
+ local currdir, sdir, sconfig, s
+ sources = sources or {}
+
+ currdir = e2tool.sourcedir(basedir, info.root)
+ for entry, re in e2lib.directory(currdir) do
+ if not entry then
+ return false, re
+ end
+
+ if basedir then
+ entry = e2lib.join(basedir, entry)
+ end
+
+ sdir = e2tool.sourcedir(entry, info.root)
+ sconfig = e2tool.sourceconfig(entry, info.root)
+ s = e2lib.stat(sdir, false)
+ if s.type == "directory" then
+ if e2lib.exists(sconfig) then
+ table.insert(sources, entry)
+ else
+ -- try sub directory
+ rc, re = gather_source_paths(info, entry, sources)
+ if not rc then
+ return false, re
+ end
+ end
+ end
+ end
+
+ return sources
+end
+
+--- Search, load and verify all source configs. On success, all sources
+--available as objects in source.sources[] etc.
+-- @param info Info table
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function source.load_source_configs(info)
+ local rc, re, e
+ local g, rawsrc, loadcnt, configs, path, src
+
+ e = err.new("error loading source configuration")
+
+ configs, re = gather_source_paths(info)
+ if not configs then
+ return false, e:cat(re)
+ end
+
+ for _,cfg in ipairs(configs) do
+ rc, re = e2tool.verify_src_res_pathname_valid_chars(cfg)
+ if not rc then
+ e:append("invalid source file name: %s", cfg)
+ e:cat(re)
+ return false, e
+ end
+
+ rawsrc = nil
+ loadcnt = 0
+ g = {
+ e2source = function(data) rawsrc = data loadcnt = loadcnt + 1 end,
+ env = info.env,
+ string = e2lib.safe_string_table(),
+ }
+
+ path = e2tool.sourceconfig(cfg, info.root)
+ rc, re = e2lib.dofile2(path, g)
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ if type(rawsrc) ~= "table" then
+ return false, e:append("source %q is missing an e2source table", cfg)
+ end
+
+ if loadcnt > 1 then
+ return false, e:append("duplicate source config in %q", cfg)
+ end
+
+ if not rawsrc.name then
+ e2lib.warnf("WDEFAULT", "`name' attribute missing in source config.")
+ e2lib.warnf("WDEFAULT", " Defaulting to directory name")
+ rawsrc.name = e2tool.src_res_path_to_name(cfg)
+ end
+
+ if rawsrc.name ~= e2tool.src_res_path_to_name(cfg) then
+ return false, e:append(
+ "source name %q must match source directory name %q",
+ rawsrc.name, e2tool.src_res_path_to_name(cfg))
+ end
+
+ rc, re = e2tool.verify_src_res_name_valid_chars(rawsrc.name)
+ if not rc then
+ e:append("invalid source name: %s", rawsrc.name)
+ e:cat(re)
+ return false, e
+ end
+
+ if source.sources[rawsrc.name] then
+ return false, e:append("duplicate source: %s", rawsrc.name)
+ end
+
+ -- source with no type field is treated as file source
+ if not rawsrc.type then
+ rawsrc.type = "files"
+ e2lib.warnf("WDEFAULT", "in source %s", rawsrc.name)
+ e2lib.warnf("WDEFAULT", " type attribute defaults to `files'")
+ end
+
+ if not source_types[rawsrc.type] then
+ return false,
+ e:append("don't know how to handle %q source", rawsrc.type)
+ end
+
+ src = source_types[rawsrc.type]
+
+ -- src:new(rawsrc)
+ rc, re = e2lib.trycall(src.new, src, rawsrc)
+ if not rc then
+ e = err.new("error in source %q", rawsrc.name)
+ return false, e:cat(re)
+ end
+
+ src = re
+ assert(type(src) == "table")
+ source.sources[src:get_name()] = src
+ end
+
+ for sourcename,_ in pairs(source.sources) do
+ table.insert(source.sources_sorted, sourcename)
+ end
+ table.sort(source.sources_sorted)
+
+ return true
+end
+
+--- Register a source class. A type can only be registered once.
+-- @param typ Source type name.
+-- @param source_class Class derived from basic_source.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function source.register_source_class(typ, source_class)
+ assert(type(typ) == "string" and typ ~= "")
+ assert(type(source_class) == "table")
+
+ if source_types[typ] then
+ return false, err.new("source %q already registered", typ)
+ end
+
+ source_types[typ] = source_class
+
+ return true
+end
+
+--- Validate licences attribute in rawsrc and set licences in src if successful.
+-- @param rawsrc e2source config table
+-- @param src Object of class basic_source.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function source.generic_source_validate_licences(rawsrc, src)
+ assert(type(rawsrc) == "table" and rawsrc.name and rawsrc.type)
+ assert(type(src) == "table")
+
+ local rc, re, licences
+
+ licences = sl.sl:new(false, true --[[unique]])
+
+ --[[if not rawsrc.licences and rawsrc.licence then
+ e2lib.warnf("WDEPRECATED", "in source %s:", src.name)
+ e2lib.warnf("WDEPRECATED",
+ " licence attribute is deprecated. Replace by licences.")
+ src.licences = src.licence
+ end]]
+
+ if rawsrc.licences == nil then
+ e2lib.warnf("WDEFAULT", "in source %s:", rawsrc.name)
+ e2lib.warnf("WDEFAULT",
+ " licences attribute missing. Defaulting to empty list.")
+ rawsrc.licences = {}
+ elseif type(rawsrc.licences) == "string" then
+ e2lib.warnf("WDEPRECATED", "in source %s:", rawsrc.name)
+ e2lib.warnf("WDEPRECATED",
+ " licences attribute is not in table format. Converting.")
+ rawsrc.licences = { rawsrc.licences }
+ end
+
+ if type(rawsrc.licences) ~= "table" then
+ return false, err.new("licences attribute must be a table")
+ end
+
+ rc, re = e2lib.vrfy_listofstrings(rawsrc.licences, "licences attribute",
+ true, false)
+ if not rc then
+ return false, re
+ end
+
+ for _,licencename in ipairs(rawsrc.licences) do
+ if not licence.licences[licencename] then
+ return false, err.new("unknown licence: %s", licencename)
+ end
+ assert(licences:insert(licencename))
+ end
+
+ src:set_licences(licences)
+
+ return true
+end
+
+--- Helper to validate and set env in src.
+-- @param rawsrc e2source config table.
+-- @param src Source object.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function source.generic_source_validate_env(rawsrc, src)
+ assert(type(rawsrc) == "table" and rawsrc.name and rawsrc.type)
+ assert(type(src) == "table")
+
+ local newenv = environment.new()
+
+ if rawsrc.env ~= nil and type(rawsrc.env) ~= "table" then
+ return false, err.new("source has invalid `env' attribute")
+ end
+
+ if not rawsrc.env then
+ e2lib.warnf("WDEFAULT",
+ "source has no `env' attribute. Defaulting to empty dictionary")
+ rawsrc.env = {}
+ end
+
+ for k, v in pairs(rawsrc.env) do
+ if type(k) ~= "string" then
+ return false, err.new(
+ "in `env' dictionary: key is not a string: %s", tostring(k))
+ elseif type(v) ~= "string" then
+ return false, err.new(
+ "in `env' dictionary: value is not a string: %s", tostring(v))
+ else
+ newenv:set(k, v)
+ end
+ end
+
+ src:set_env(newenv)
+
+ return true
+end
+
+--- Helper to validate server.
+-- @param rawsrc e2source config table
+-- @param ismandatory Whether rawsrc containing a server attr is mandatory.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function source.generic_source_validate_server(rawsrc, ismandatory)
+ assert(type(rawsrc) == "table" and rawsrc.name and rawsrc.type)
+ assert(type(ismandatory) == "boolean")
+
+ local info = e2tool.info()
+
+ if ismandatory and rawsrc.server == nil then
+ return false, err.new("source has no `server' attribute")
+ end
+
+ if rawsrc.server ~= nil and type(rawsrc.server) ~= "string" then
+ return false, err.new("'server' attribute must be a string")
+ end
+
+ if rawsrc.server and (not cache.valid_server(info.cache, rawsrc.server)) then
+ return false, err.new("invalid server: %s", rawsrc.server)
+ end
+
+ return true
+end
+
+--- Helper to validate working attribute.
+-- @param rawsrc e2source config table
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function source.generic_source_validate_working(rawsrc)
+ assert(type(rawsrc) == "table" and rawsrc.name and rawsrc.type)
+
+ if rawsrc.working ~= nil and not type(rawsrc.working) == "string" then
+ return false, err.new("'working' attribute must be a string")
+ end
+
+ if rawsrc.working == nil then
+ rawsrc.working = e2lib.join("in", rawsrc.name)
+
+ e2lib.warnf("WDEFAULT", "in source %s:", rawsrc.name)
+ e2lib.warnf("WDEFAULT", " `working' attribute defaults to '%s'.",
+ rawsrc.working)
+ end
+
+ return true
+end
+
+return strict.lock(source)
+
+-- vim:sw=4:sts=4:et:
local cvs = {}
local cache = require("cache")
+local class = require("class")
local e2lib = require("e2lib")
+local e2tool = require("e2tool")
local eio = require("eio")
local err = require("err")
local hash = require("hash")
local strict = require("strict")
local tools = require("tools")
local url = require("url")
+local source = require("source")
plugin_descriptor = {
description = "CVS SCM Plugin",
- init = function (ctx) scm.register("cvs", cvs) return true end,
+ init = function (ctx)
+ local rc, re
+
+ rc, re = source.register_source_class("cvs", cvs.cvs_source)
+ if not rc then
+ return false, re
+ end
+
+ rc, re = scm.register("cvs", cvs)
+ if not rc then
+ return false, re
+ end
+
+ return true
+ end,
exit = function (ctx) return true end,
}
+cvs.cvs_source = class("cvs_source", source.basic_source)
+
local function cvs_tool(argv, workdir)
local rc, re, cvscmd, cvsflags, rsh
return e2lib.callcmd_log(cvscmd, workdir, { CVS_RSH=rsh })
end
---- validate source configuration, log errors to the debug log
--- @param info the info table
--- @param sourcename the source name
--- @return bool
-function cvs.validate_source(info, sourcename)
- local rc, re = scm.generic_source_validate(info, sourcename)
+
+function cvs.cvs_source:initialize(rawsrc)
+ assert(type(rawsrc) == "table")
+ assert(type(rawsrc.name) == "string" and #rawsrc.name > 0)
+ assert(type(rawsrc.type) == "string" and rawsrc.type == "cvs")
+
+ local rc, re
+
+ source.basic_source.initialize(self, rawsrc)
+
+ self._branch = false
+ self._cvsroot = false
+ self._module = false
+ self._server = false
+ self._tag = false
+ self._working = false
+ self._sourceids = {
+ ["working-copy"] = "working-copy",
+ }
+
+ rc, re = e2lib.vrfy_dict_exp_keys(rawsrc, "e2source", {
+ "branch",
+ "cvsroot",
+ "env",
+ "licences",
+ "module",
+ "name",
+ "server",
+ "tag",
+ "type",
+ "working",
+ })
if not rc then
- -- error in generic configuration. Don't try to go on.
- return false, re
+ error(re)
end
- local src = info.sources[ sourcename ]
- if not src.sourceid then
- src.sourceid = {}
+
+ rc, re = source.generic_source_validate_licences(rawsrc, self)
+ if not rc then
+ error(re)
end
- local e = err.new("in source %s:", sourcename)
- rc, re = scm.generic_source_default_working(info, sourcename)
+
+ rc, re = source.generic_source_validate_env(rawsrc, self)
if not rc then
- return false, e:cat(re)
+ error(re)
end
- e:setcount(0)
- -- XXX should move the default value out of the validate function
- if not src.server then
- e:append("source has no `server' attribute")
+
+ rc, re = source.generic_source_validate_server(rawsrc, true)
+ if not rc then
+ error(re)
end
- if not src.licences then
- e:append("source has no `licences' attribute")
+ self._server = rawsrc.server
+
+ rc, re = source.generic_source_validate_working(rawsrc)
+ if not rc then
+ error(re)
end
- if not src.cvsroot then
+ self._working = rawsrc.working
+
+ if rawsrc.cvsroot == nil then
e2lib.warnf("WDEFAULT", "in source %s:", sourcename)
e2lib.warnf("WDEFAULT",
" source has no `cvsroot' attribute, defaulting to the server path")
- src.cvsroot = "."
- end
- if not src.cvsroot then
- e:append("source has no `cvsroot' attribute")
- end
- if src.remote then
- e:append("source has `remote' attribute, not allowed for cvs sources")
+ self._cvsroot = "."
+ elseif type(rawsrc.cvsroot) == "string" then
+ self._cvsroot = rawsrc.cvsroot
+ else
+ error(err.new("'cvsroot' must be a string"))
end
- if not src.branch then
- e:append("source has no `branch' attribute")
+
+ for _,attr in ipairs({ "branch", "module", "tag" }) do
+ if rawsrc[attr] == nil then
+ error(err.new("source has no `%s' attribute", attr))
+ elseif type(rawsrc[attr]) ~= "string" then
+ error(err.new("'%s' must be a string", attr))
+ elseif rawsrc[attr] == "" then
+ error(err.new("'%s' may not be empty", attr))
+ end
end
- if type(src.tag) ~= "string" then
- e:append("source has no `tag' attribute or tag attribute has wrong type")
+ self._branch = rawsrc.branch
+ self._module = rawsrc.module
+ self._tag = rawsrc.tag
+end
+
+function cvs.cvs_source:get_working()
+ assert(type(self._working) == "string")
+
+ return self._working
+end
+
+function cvs.cvs_source:get_module()
+ assert(type(self._module) == "string")
+
+ return self._module
+end
+
+function cvs.cvs_source:get_branch()
+ assert(type(self._branch) == "string")
+
+ return self._branch
+end
+
+function cvs.cvs_source:get_tag()
+ assert(type(self._tag) == "string")
+
+ return self._tag
+end
+
+function cvs.cvs_source:get_server()
+ assert(type(self._server) == "string")
+
+ return self._server
+end
+
+function cvs.cvs_source:get_cvsroot()
+ assert(type(self._cvsroot) == "string")
+
+ return self._cvsroot
+end
+
+function cvs.cvs_source:sourceid(sourceset)
+ assert(type(sourceset) == "string" and #sourceset > 0)
+
+ local rc, re, hc, lid, info, licences
+
+ if self._sourceids[sourceset] then
+ return self._sourceids[sourceset]
end
- if not src.module then
- e:append("source has no `module' attribute")
+
+ info = e2tool.info()
+ assert(type(info) == "table")
+
+ hc = hash.hash_start()
+ hash.hash_line(hc, self._name)
+ hash.hash_line(hc, self._type)
+ hash.hash_line(hc, self._env:id())
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ lid, re = licence.licences[licencename]:licenceid(info)
+ if not lid then
+ return false, re
+ end
+ hash.hash_line(hc, lid)
end
- if not src.working then
- e:append("source has no `working' attribute")
+ -- cvs specific
+ if sourceset == "tag" and self._tag ~= "^" then
+ -- we rely on tags being unique with cvs
+ hash.hash_line(hc, self._tag)
+ else
+ -- the old function took a hash of the CVS/Entries file, but
+ -- forgot the subdirecties' CVS/Entries files. We might
+ -- reimplement that once...
+ return false, err.new("cannot calculate sourceid for source set %s",
+ sourceset)
end
- local rc, re = tools.check_tool("cvs")
- if not rc then
- e:cat(re)
+ hash.hash_line(hc, self._server)
+ hash.hash_line(hc, self._cvsroot)
+ hash.hash_line(hc, self._module)
+
+ self._sourceids[sourceset] = hash.hash_finish(hc)
+
+ return self._sourceids[sourceset]
+end
+
+function cvs.cvs_source:display()
+ local licences
+ local d = {}
+
+ self:sourceid("tag")
+ self:sourceid("branch")
+
+ table.insert(d, string.format("type = %s", self:get_type()))
+ table.insert(d, string.format("branch = %s", self._branch))
+ table.insert(d, string.format("tag = %s", self._tag))
+ table.insert(d, string.format("server = %s", self._server))
+ table.insert(d, string.format("cvsroot = %s", self._cvsroot))
+ table.insert(d, string.format("module = %s", self._module))
+ table.insert(d, string.format("working = %s", self._working))
+
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ table.insert(d, string.format("licence = %s", licencename))
end
- if e:getcount() > 0 then
- return false, e
+
+ for sourceset, sid in pairs(self._sourceids) do
+ if sid then
+ table.insert(d, string.format("sourceid [%s] = %s", sourceset, sid))
+ end
end
- return true, nil
+
+ return d
end
--- Build the cvsroot string.
local function mkcvsroot(info, sourcename)
local cvsroot, src, surl, u, re
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
- surl, re = cache.remote_url(info.cache, src.server, src.cvsroot)
+ surl, re = cache.remote_url(info.cache, src:get_server(), src:get_cvsroot())
if not surl then
return false, e:cat(re)
end
local rc, re, e, src, cvsroot, workdir, argv
e = err.new("fetching source failed: %s", sourcename)
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
cvsroot, re = mkcvsroot(info, sourcename)
if not cvsroot then
-- split the working directory into dirname and basename as some cvs clients
-- don't like slashes (e.g. in/foo) in their checkout -d<path> argument
- workdir = e2lib.dirname(e2lib.join(info.root, src.working))
+ workdir = e2lib.dirname(e2lib.join(info.root, src:get_working()))
argv = {
"-d", cvsroot,
"checkout",
"-R",
- "-d", e2lib.basename(src.working),
+ "-d", e2lib.basename(src:get_working()),
}
-- always fetch the configured branch, as we don't know the build mode here.
-- HEAD has special meaning to cvs
- if src.branch ~= "HEAD" then
+ if src:get_branch() ~= "HEAD" then
table.insert(argv, "-r")
- table.insert(argv, src.branch)
+ table.insert(argv, src:get_branch())
end
- table.insert(argv, src.module)
+ table.insert(argv, src:get_module())
rc, re = cvs_tool(argv, workdir)
if not rc or rc ~= 0 then
return true
end
-function cvs.prepare_source(info, sourcename, source_set, buildpath)
+function cvs.prepare_source(info, sourcename, sourceset, buildpath)
local rc, re, e, src, cvsroot, argv
e = err.new("cvs.prepare_source failed")
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
cvsroot, re = mkcvsroot(info, sourcename)
if not cvsroot then
return false, re
end
- if source_set == "tag" or source_set == "branch" then
+ if sourceset == "tag" or sourceset == "branch" then
argv = {
"-d", cvsroot,
"export", "-R",
- "-d", src.name,
+ "-d", src:get_name(),
"-r",
}
- if source_set == "branch" or
- (source_set == "lazytag" and src.tag == "^") then
- table.insert(argv, src.branch)
- elseif (source_set == "tag" or source_set == "lazytag") and
- src.tag ~= "^" then
- table.insert(argv, src.tag)
+ if sourceset == "branch" or
+ (sourceset == "lazytag" and src:get_tag() == "^") then
+ table.insert(argv, src:get_branch())
+ elseif (sourceset == "tag" or sourceset == "lazytag") and
+ src:get_tag() ~= "^" then
+ table.insert(argv, src:get_tag())
else
return false, e:cat(err.new("source set not allowed"))
end
- table.insert(argv, src.module)
+ table.insert(argv, src:get_module())
rc, re = cvs_tool(argv, buildpath)
if not rc or rc ~= 0 then
return false, e:cat(re)
end
- elseif source_set == "working-copy" then
- rc, re = e2lib.cp(e2lib.join(info.root, src.working),
- e2lib.join(buildpath, src.name), true)
+ elseif sourceset == "working-copy" then
+ rc, re = e2lib.cp(e2lib.join(info.root, src:get_working()),
+ e2lib.join(buildpath, src:get_name()), true)
if not rc then
return false, e:cat(re)
end
local rc, re, e, src, workdir, argv
e = err.new("updating source '%s' failed", sourcename)
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
- workdir = e2lib.join(info.root, src.working)
+ workdir = e2lib.join(info.root, src:get_working())
argv = { "update", "-R" }
rc, re = cvs_tool(argv, workdir)
return false, e:cat(re)
end
- return true, nil
+ return true
end
function cvs.working_copy_available(info, sourcename)
- local src = info.sources[sourcename]
- local dir = string.format("%s/%s", info.root, src.working)
+ local src = source.sources[sourcename]
+ local dir = e2lib.join(info.root, src:get_working())
return e2lib.isdir(dir)
end
return true
end
---- create a table of lines for display
--- @param info the info structure
--- @param sourcename string
--- @return a table, nil on error
--- @return an error object on failure
-function cvs.display(info, sourcename)
- local src = info.sources[sourcename]
- local rc, re
- local display = {}
-
- display[1] = string.format("type = %s", src.type)
- display[2] = string.format("branch = %s", src.branch)
- display[3] = string.format("tag = %s", src.tag)
- display[4] = string.format("server = %s", src.server)
- display[5] = string.format("cvsroot = %s", src.cvsroot)
- display[6] = string.format("module = %s", src.module)
- display[7] = string.format("working = %s", src.working)
- local i = 8
- for _,l in ipairs(src.licences) do
- display[i] = string.format("licence = %s", l)
- i = i + 1
- end
- for k,v in pairs(src.sourceid) do
- if v then
- display[i] = string.format("sourceid [%s] = %s", k, v)
- i = i + 1
- end
- end
- return display, nil
-end
-
-function cvs.sourceid(info, sourcename, source_set)
- local src = info.sources[sourcename]
- local rc, re, lid
-
- if source_set == "working-copy" then
- src.sourceid[source_set] = "working-copy"
- end
- if src.sourceid[source_set] then
- return true, nil, src.sourceid[source_set]
- end
- local e = err.new("calculating sourceid failed for source %s",
- sourcename)
- local hc = hash.hash_start()
- hash.hash_line(hc, src.name)
- hash.hash_line(hc, src.type)
- hash.hash_line(hc, src._env:id())
- for _,ln in ipairs(src.licences) do
- lid, re = licence.licences[ln]:licenceid(info)
- if not lid then
- return false, e:cat(re)
- end
- hash.hash_line(hc, lid)
- end
- -- cvs specific
- if source_set == "tag" and src.tag ~= "^" then
- -- we rely on tags being unique with cvs
- hash.hash_line(hc, src.tag)
- else
- -- the old function took a hash of the CVS/Entries file, but
- -- forgot the subdirecties' CVS/Entries files. We might
- -- reimplement that once...
- e:append("cannot calculate sourceid for source set %s",
- source_set)
- return false, e
- end
- hash.hash_line(hc, src.server)
- hash.hash_line(hc, src.cvsroot)
- hash.hash_line(hc, src.module)
- -- skip src.working
- src.sourceid[source_set] = hash.hash_finish(hc)
- return true, nil, src.sourceid[source_set]
-end
-
function cvs.toresult(info, sourcename, sourceset, directory)
-- <directory>/source/<sourcename>.tar.gz
-- <directory>/makefile
if not rc then
return false, e:cat(re)
end
- local src = info.sources[sourcename]
+ local src = source.sources[sourcename]
-- write makefile
local makefile = "Makefile"
local source = "source"
return false, e:cat(re)
end
-- create a tarball in the final location
- local archive = string.format("%s.tar.gz", src.name)
+ local archive = string.format("%s.tar.gz", src:get_name())
rc, re = e2lib.tar({ "-C", tmpdir ,"-czf", sourcedir .. "/" .. archive,
sourcename })
if not rc then
-- write licences
local destdir = string.format("%s/licences", directory)
local fname = string.format("%s/%s.licences", destdir, archive)
- local licence_list = table.concat(src.licences, "\n") .. "\n"
+ local licenses = src:get_licences()
+ local licence_list = licenses:concat_sorted("\n").."\n"
+
rc, re = e2lib.mkdir_recursive(destdir)
if not rc then
return false, e:cat(re)
local files = {}
local cache = require("cache")
+local class = require("class")
local e2lib = require("e2lib")
local e2tool = require("e2tool")
local eio = require("eio")
+local environment = require("environment")
local err = require("err")
local hash = require("hash")
+local licence = require("licence")
local scm = require("scm")
+local sl = require("sl")
+local source = require("source")
local strict = require("strict")
local tools = require("tools")
-local licence = require("licence")
+
plugin_descriptor = {
description = "Files SCM Plugin",
- init = function (ctx) scm.register("files", files) return true end,
+ init = function (ctx)
+ local rc, re
+
+ rc, re = source.register_source_class("files", files.files_source)
+ if not rc then
+ return false, re
+ end
+
+ rc, re = scm.register("files", files)
+ if not rc then
+ return false, re
+ end
+
+ return true
+ end,
exit = function (ctx) return true end,
}
---- validate source configuration, log errors to the debug log
--- @param info the info table
--- @param sourcename the source name
--- @return bool
-function files.validate_source(info, sourcename)
- local rc1 = true -- the return value
- local rc, e = scm.generic_source_validate(info, sourcename)
+files.files_source = class("files_source", source.basic_source)
+
+function files.files_source:initialize(rawsrc)
+ assert(type(rawsrc) == "table")
+ assert(type(rawsrc.name) == "string" and #rawsrc.name > 0)
+ assert(type(rawsrc.type) == "string" and rawsrc.type == "files")
+
+ local rc, re, e, info
+
+ source.basic_source.initialize(self, rawsrc)
+
+ self._files = {}
+ self._sourceid = false
+
+ rc, re = e2lib.vrfy_dict_exp_keys(rawsrc, "e2source config", {
+ "env",
+ "file",
+ "licences",
+ "name",
+ "server",
+ "type",
+ })
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_licences(rawsrc, self)
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_env(rawsrc, self)
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_server(rawsrc, false)
if not rc then
- return false, e
+ error(re)
end
- e = err.new("in source %s:", sourcename)
- e:setcount(0)
- local src = info.sources[ sourcename ]
- if type(src.file) ~= "table" then
- return false, e:cat(err.new("source has no valid `file' attribute"))
+
+ if type(rawsrc.file) ~= "table" then
+ error(err.new("`file' attribute must be a table"))
end
- for _,f in pairs(src.file) do
+ e = err.new("error in file list of source")
+ for _,f in ipairs(rawsrc.file) do
if type(f) ~= "table" then
- e:append("%s: source has invalid file entry in `file' attribute",
- sourcename)
- break
- end
- -- catch deprecated configuration
- if f.name then
- e:append("source has file entry with `name' attribute")
+ error(e:append("`file' attribute must be a table"))
end
- if (not f.licences) and src.licences then
- f.licences = src.licences
- end
- if (not f.server) and src.server then
- f.server = src.server
- end
- if not f.licences then
- e:append("source has file entry without `licences' attribute")
+
+ rc, re = e2lib.vrfy_dict_exp_keys(f, "e2source config",
+ {
+ "copy",
+ "licences",
+ "location",
+ "patch",
+ "server",
+ "sha1",
+ "unpack",
+ })
+ if not rc then
+ error(e:cat(re))
end
- for _,l in ipairs(f.licences) do
- if not licence.licences[l] then
- e:append("invalid licence assigned to file: %s", l)
- end
+
+
+ if (not f.server) and rawsrc.server then
+ f.server = rawsrc.server
end
+
+ info = e2tool.info()
+
if not f.server then
- e:append("source has file entry without `server' attribute")
+ error(e:append("file entry without `server' attribute"))
end
if f.server and (not cache.valid_server(info.cache, f.server)) then
- e:append("invalid server: %s", f.server)
+ error(e:append("invalid server: %s", f.server))
end
if not f.location then
- e:append("source has file entry without `location' attribute")
+ error(e:append("file entry without `location' attribute"))
end
if f.server ~= info.root_server_name and not f.sha1 then
- e:append("source has file entry for remote file without `sha1` "..
- "attribute")
+ error(e:append("file entry for remote file without "..
+ "`sha1` attribute"))
end
local attrcnt = 0
attrcnt = attrcnt + 1
if type(f[attr]) ~= "string" then
- e:append("'%s' in file entry of source must be a string", attr)
- break
+ error(e:append(
+ "'%s' in file entry of source must be a string", attr))
end
end
end
if attrcnt == 0 then
- e:append("source has file entry without `unpack, copy or patch' " ..
- "attribute")
+ error(e:append("file entry without "..
+ "unpack, copy or patch attribute"))
elseif attrcnt > 1 then
- e:append("source has file entry with conflicting unpack, copy or"..
- " patch attributes")
+ error(e:append("file entry with conflicting "..
+ "unpack, copy or patch attributes"))
end
- if f.checksum_file then
- e2lib.warnf("WDEPRECATED", "in source %s:", sourcename)
- e2lib.warnf("WDEPRECATED",
- " checksum_file attribute is deprecated and no longer used")
- f.checksum_file = nil
+ assert(type(f.location) == "string" and f.location ~= "")
+ assert(type(f.server) == "string" and f.server ~= "")
+ assert(f.sha1 == nil or (type(f.sha1) == "string" and #f.sha1 == 40))
+
+ -- per file licences --
+ local laerr = string.format("%s:%s licences attribute",
+ f.server, f.location)
+ local llist, licences
+
+ if f.licences == nil then
+ f.licences = self:get_licences():copy()
+ elseif type(f.licences == "table") then
+ rc, re = e2lib.vrfy_listofstrings(f.licences, laerr, true, false)
+ if not rc then
+ error(e:cat(re))
+ end
+
+ licences = self:get_licences()
+ llist = sl.sl:new(false, true)
+
+ for _,licencename in ipairs(f.licences) do
+ if not licence.licences[licencename] then
+ error(e:append("%s has unknown licence: %q",
+ laerr, licencename))
+ end
+
+ -- Make sure the _licences list contains every licence in the
+ -- entire source. Duplicates are rejected by unique string list.
+ licences:insert(licencename)
+ assert(llist:insert(licencename))
+ end
+
+ self:set_licences(licences)
+ f.licences = llist
+ else
+ error(e:append("%s must be a table", laerr))
+ end
+
+ if f.unpack then
+ assert(type(f.unpack) == "string")
+
+ table.insert(self._files, {
+ location=f.location,
+ server=f.server,
+ sha1=f.sha1,
+ unpack=f.unpack,
+ licences=f.licences,
+ })
+ elseif f.copy then
+ assert(type(f.copy) == "string")
+
+ table.insert(self._files, {
+ location=f.location,
+ server=f.server,
+ sha1=f.sha1,
+ copy=f.copy,
+ licences=f.licences,
+ })
+ elseif f.patch then
+ assert(type(f.patch) == "string")
+
+ table.insert(self._files, {
+ location=f.location,
+ server=f.server,
+ sha1=f.sha1,
+ patch=f.patch,
+ licences=f.licences,
+ })
+ else
+ assert("internal error" == true)
end
end
+end
+
+function files.files_source:file_iter()
+ local i = 0
+
+ return function ()
+ i = i + 1
+
+ if self._files[i] then
+ -- return a copy so nobody can mess with the internals
+ local f = {
+ location = self._files[i].location,
+ server = self._files[i].server,
+ sha1 = self._files[i].sha1,
+ licences = self._files[i].licences:copy()
+ }
+ for _,attr in ipairs({ "copy", "unpack", "patch" }) do
+ if self._files[i][attr] then
+ f[attr] = self._files[i][attr]
+ break
+ end
+ end
+ return f
+ end
- if e:getcount() > 0 then
- return false, e
+ return nil
end
- return true, nil
+end
+
+function files.files_source:sourceid(sourceset --[[always ignored for files]])
+ local hc, info, licences
+
+ if self._sourceid then
+ return self._sourceid
+ end
+
+ info = e2tool.info()
+ assert(info)
+
+ hc = hash.hash_start()
+ hash.hash_line(hc, self._name)
+ hash.hash_line(hc, self._type)
+ hash.hash_line(hc, self._env:id())
+
+ for f in self:file_iter() do
+ local fileid, re = e2tool.fileid(info, f)
+ if not fileid then
+ return false, re
+ end
+ hash.hash_line(hc, fileid)
+ hash.hash_line(hc, f.location)
+ hash.hash_line(hc, f.server)
+ hash.hash_line(hc, tostring(f.unpack))
+ hash.hash_line(hc, tostring(f.patch))
+ hash.hash_line(hc, tostring(f.copy))
+
+ -- per file licence list
+ for licencename in f.licences:iter_sorted() do
+ local lid, re = licence.licences[licencename]:licenceid(info)
+ if not lid then
+ return false, re
+ end
+ hash.hash_line(hc, lid)
+ end
+ end
+
+ self._sourceid = hash.hash_finish(hc)
+
+ return self._sourceid
+end
+
+--- create a table of lines for display
+-- @return a table
+function files.files_source:display()
+ local s, sid, d, licences
+
+ self:sourceid()
+
+ d = {}
+ table.insert(d, string.format("type = %s", self:get_type()))
+
+ for f in self:file_iter() do
+ s = string.format("file = %s:%s", f.server, f.location)
+ table.insert(d, s)
+ end
+
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ table.insert(d, string.format("licence = %s", licencename))
+ end
+
+ if self._sourceid then
+ table.insert(d, string.format("sourceid = %s", self._sourceid))
+ end
+
+ return d
end
--- cache files for a source
-- @return bool
-- @return nil, an error string on error
function files.cache_source(info, sourcename)
- local rc, e
- local s = info.sources[sourcename]
+ local rc, re
+ local src = source.sources[sourcename]
+
-- cache all files for this source
- for i,f in pairs(s.file) do
+ for f in src:file_iter() do
e2lib.logf(4, "files.cache_source: caching file %s:%s", f.server,
f.location)
local flags = { cache = true }
if f.server ~= info.root_server_name then
- local rc, e = cache.cache_file(info.cache, f.server,
- f.location, flags)
+ rc, re = cache.cache_file(info.cache, f.server, f.location, flags)
if not rc then
- return false, e
+ return false, re
end
else
e2lib.logf(4, "not caching %s:%s (stored locally)", f.server,
f.location)
end
end
- return true, nil
+ return true
end
function files.fetch_source(info, sourcename)
local rc, re
local e = err.new("error preparing source: %s", sourcename)
local symlink = nil
- local s = info.sources[sourcename]
- for _,file in ipairs(info.sources[sourcename].file) do
+ local src = source.sources[sourcename]
+
+ for file in src:file_iter() do
if file.sha1 then
rc, re = e2tool.verify_hash(info, file.server, file.location, file.sha1)
if not rc then
return true, nil
end
---- create a table of lines for display
--- @param info the info structure
--- @param sourcename string
--- @return a table, nil on error
--- @return an error string on failure
-function files.display(info, sourcename)
- local src = info.sources[sourcename]
- local display = {}
-
- display[1] = string.format("type = %s", src.type)
- local i = 2
- for _,f in pairs(src.file) do
- display[i] = string.format("file = %s:%s", f.server, f.location)
- i = i + 1
- end
- for _,l in ipairs(src.licences) do
- display[i] = string.format("licence = %s", l)
- i = i + 1
- end
- if src.sourceid then
- display[i] = string.format("sourceid = %s", src.sourceid)
- i = i + 1
- end
- return display
-end
-
---- calculate an id for a source
--- @param info
--- @param sourcename
--- @param sourceset
--- @return string: the source id, nil on error
--- @return an error string on error
-function files.sourceid(info, sourcename, sourceset)
- local rc, re
- local e = err.new("error calculating sourceid for source: %s",
- sourcename)
- local src = info.sources[sourcename]
- if src.sourceid then
- return true, nil, src.sourceid
- end
- -- sourceset is ignored for files sources
- local hc = hash.hash_start()
- hash.hash_line(hc, src.name)
- hash.hash_line(hc, src.type)
- hash.hash_line(hc, src._env:id())
- for _,ln in ipairs(src.licences) do
- local lid, re = licence.licences[ln]:licenceid(info)
- if not lid then
- return false, re
- end
- hash.hash_line(hc, lid)
- end
- for _,f in ipairs(src.file) do
- local fileid, re = e2tool.fileid(info, f)
- if not fileid then
- return false, e:cat(re)
- end
- hash.hash_line(hc, fileid)
- hash.hash_line(hc, f.location)
- hash.hash_line(hc, f.server)
- hash.hash_line(hc, tostring(f.unpack))
- hash.hash_line(hc, tostring(f.patch))
- hash.hash_line(hc, tostring(f.copy))
- end
- src.sourceid = hash.hash_finish(hc)
- return true, nil, src.sourceid
-end
-
--- Create a source result containing the generated Makefile and files
-- belonging to the source, for use with collect_project.
-- Result refers to a collection of files to recreate an e2source for
function files.toresult(info, sourcename, sourceset, directory)
local rc, re, out
local e = err.new("converting result failed")
- local s = info.sources[sourcename]
+ local src = source.sources[sourcename]
local source = "source" -- directory to store source files in
local makefile = e2lib.join(directory, "Makefile")
out = { ".PHONY: place\n\nplace:\n" }
- for _,file in ipairs(s.file) do
+ for file in src:file_iter() do
e2lib.logf(4, "export file: %s", file.location)
local destdir = string.format("%s/%s", directory, source)
local destname = nil
-- write licences
local destdir = string.format("%s/licences", directory)
local fname = string.format("%s/%s.licences", destdir,
- e2lib.basename(file.location))
- local licence_list = table.concat(file.licences, "\n") .. "\n"
+ e2lib.basename(file.location))
+ local licence_list = file.licences:concat_sorted("\n") .. "\n"
rc, re = e2lib.mkdir_recursive(destdir)
if not rc then
return false, e:cat(re)
local git = {}
local cache = require("cache")
+local class = require("class")
local e2lib = require("e2lib")
local e2option = require("e2option")
local e2tool = require("e2tool")
local strict = require("strict")
local tools = require("tools")
local url = require("url")
+local source = require("source")
--- Initialize git plugin.
-- @param ctx Plugin context. See plugin module.
local function git_plugin_init(ctx)
local rc, re
+ rc, re = source.register_source_class("git", git.git_source)
+ if not rc then
+ return false, re
+ end
+
rc, re = scm.register("git", git)
if not rc then
return false, re
exit = function (ctx) return true end,
}
+git.git_source = class("git_source", source.basic_source)
+
+function git.git_source:initialize(rawsrc)
+ assert(type(rawsrc) == "table")
+ assert(type(rawsrc.name) == "string" and rawsrc.name ~= "")
+ assert(type(rawsrc.type) == "string" and rawsrc.type ~= "")
+
+ local rc, re
+
+ source.basic_source.initialize(self, rawsrc)
+
+ self._server = false
+ self._location = false
+ self._tag = false
+ self._branch = false
+ self._working = false
+ self._sourceids = {
+ ["working-copy"] = "working-copy",
+ }
+ self._commitids = {}
+
+ rc, re = e2lib.vrfy_dict_exp_keys(rawsrc, "e2source", {
+ "branch",
+ "env",
+ "licences",
+ "location",
+ "name",
+ "server",
+ "tag",
+ "type",
+ "working",
+ })
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_licences(rawsrc, self)
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_env(rawsrc, self)
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_server(rawsrc, true)
+ if not rc then
+ error(re)
+ end
+ self._server = rawsrc.server
+
+ rc, re = source.generic_source_validate_working(rawsrc)
+ if not rc then
+ error(re)
+ end
+ self._working = rawsrc.working
+
+ for _,attr in ipairs({ "branch", "location", "tag" }) do
+ if rawsrc[attr] == nil then
+ error(err.new("source has no `%s' attribute", attr))
+ elseif type(rawsrc[attr]) ~= "string" then
+ error(err.new("'%s' must be a string", attr))
+ elseif rawsrc[attr] == "" then
+ error(err.new("'%s' may not be empty", attr))
+ end
+ end
+
+ self._branch = rawsrc.branch
+ self._location = rawsrc.location
+ self._tag = rawsrc.tag
+end
+
+function git.git_source:get_server()
+ assert(type(self._server) == "string")
+
+ return self._server
+end
+
+function git.git_source:get_location()
+ assert(type(self._location) == "string")
+
+ return self._location
+end
+
+function git.git_source:get_working()
+ assert(type(self._working) == "string")
+
+ return self._working
+end
+
+function git.git_source:get_branch()
+ assert(type(self._branch) == "string")
+
+ return self._branch
+end
+
+
+function git.git_source:get_tag()
+ assert(type(self._tag) == "string")
+
+ return self._tag
+end
+
+function git.git_source:sourceid(sourceset)
+ assert(type(sourceset) == "string" and #sourceset > 0,
+ "sourceset arg invalid")
+
+ local rc, re, info, id, hc, licences
+
+ if self._sourceids[sourceset] then
+ return self._sourceids[sourceset]
+ end
+
+ info = e2tool.info()
+ assert(info)
+
+ rc, re, id = git.git_commit_id(info, self._name, sourceset,
+ e2option.opts["check-remote"])
+ if not rc then
+ return false, re
+ end
+
+ hc = hash.hash_start()
+ hash.hash_line(hc, self._name)
+ hash.hash_line(hc, self._type)
+ hash.hash_line(hc, self._env:id())
+
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ local lid, re = licence.licences[licencename]:licenceid(info)
+ if not lid then
+ return false, re
+ end
+ hash.hash_line(hc, lid)
+ end
+
+ hash.hash_line(hc, self._server)
+ hash.hash_line(hc, self._location)
+ hash.hash_line(hc, self._working)
+ hash.hash_line(hc, id)
+ self._commitids[sourceset] = id
+ self._sourceids[sourceset] = hash.hash_finish(hc)
+
+ return self._sourceids[sourceset]
+end
+
+function git.git_source:display()
+ local rev_tag, rev_branch, licences
+
+ -- try to calculte the sourceid, but do not care if it fails.
+ -- working copy might be unavailable
+ self:sourceid("tag")
+ self:sourceid("branch")
+
+ rev_tag = ""
+ rev_branch = ""
+ if self._commitids["tag"] then
+ rev_tag = string.format("[%s...]", self._commitids["tag"]:sub(1,8))
+ end
+ if self._commitids["branch"] then
+ rev_branch = string.format("[%s...]", self._commitids["branch"]:sub(1,8))
+ end
+ local d = {}
+ table.insert(d, string.format("type = %s", self:get_type()))
+ table.insert(d, string.format("branch = %-15s %s", self._branch, rev_branch))
+ table.insert(d, string.format("tag = %-15s %s", self._tag, rev_tag))
+ table.insert(d, string.format("server = %s", self._server))
+ table.insert(d, string.format("location = %s", self._location))
+ table.insert(d, string.format("working = %s", self._working))
+
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ table.insert(d, string.format("licence = %s", licencename))
+ end
+
+ for sourceset, sid in pairs(self._sourceids) do
+ if sid then
+ table.insert(d, string.format("sourceid [%s] = %s", sourceset, sid))
+ end
+ end
+
+ return d
+end
+
--- Return the git commit ID of the specified source configuration. Specific to
-- sources of type git, useful for writing plugins.
-- @param info Info table.
local rc, re, e, src, id, fr, gitdir, ref
e = err.new("getting commit ID failed for source: %s", sourcename)
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
rc, re = scm.working_copy_available(info, sourcename)
if not rc then
return false, e:cat(re)
end
- gitdir = e2lib.join(info.root, src.working, ".git")
+ gitdir = e2lib.join(info.root, src:get_working(), ".git")
- if sourceset == "branch" or (sourceset == "lazytag" and src.tag == "^") then
- ref = string.format("refs/heads/%s", src.branch)
+ if sourceset == "branch" or (sourceset == "lazytag" and src:get_tag() == "^") then
+ ref = string.format("refs/heads/%s", src:get_branch())
rc, re, id = generic_git.lookup_id(gitdir, false, ref)
if not rc then
return false, e:cat(re)
end
- elseif sourceset == "tag" or (sourceset == "lazytag" and src.tag ~= "^") then
- ref = string.format("refs/tags/%s", src.tag)
+ elseif sourceset == "tag" or (sourceset == "lazytag" and src:get_tag() ~= "^") then
+ ref = string.format("refs/tags/%s", src:get_tag())
rc, re, id = generic_git.lookup_id(gitdir, false, ref)
if not rc then
end
if id and check_remote then
- rc, re = generic_git.verify_remote_tag(gitdir, src.tag)
+ rc, re = generic_git.verify_remote_tag(gitdir, src:get_tag())
if not rc then
return false, e:cat(re)
end
if not id then
re = err.new("can't get git commit ID for ref %q from repository %q",
- ref, src.working)
+ ref, src:get_working())
return false, e:cat(re)
end
return true, nil, id
end
---- validate source configuration, log errors to the debug log
--- @param info the info table
--- @param sourcename the source name
--- @return bool
--- @return an error object on error
-function git.validate_source(info, sourcename)
- local rc, re = scm.generic_source_validate(info, sourcename)
- if not rc then
- -- error in generic configuration. Don't try to go on.
- return false, re
- end
- local src = info.sources[ sourcename ]
- local e = err.new("in source %s:", sourcename)
- rc, re = scm.generic_source_default_working(info, sourcename)
- if not rc then
- return false, e:cat(re)
- end
- e:setcount(0)
- -- catch deprecated attributes
- if src.remote then
- e:append("source has deprecated `remote' attribute")
- end
- if not src.server then
- e:append("source has no `server' attribute")
- end
- if src.server and (not cache.valid_server(info.cache, src.server)) then
- e:append("invalid server: %s", src.server)
- end
- if not src.licences then
- e:append("source has no `licences' attribute")
- end
- if not src.branch then
- e:append("source has no `branch' attribute")
- end
- if type(src.tag) ~= "string" then
- e:append("source has no `tag' attribute or tag attribute has wrong type")
- end
- if not src.location then
- e:append("source has no `location' attribute")
- end
- if not src.working then
- e:append("source has no `working' attribute")
- end
- if e:getcount() > 0 then
- return false, e
- end
- return true, nil
-end
-
--- update a working copy
-- @param info the info structure
-- @param sourcename string
function git.update(info, sourcename)
local e, rc, re, src, gitwc, gitdir, argv, id, branch, remote
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
e = err.new("updating source '%s' failed", sourcename)
rc, re = scm.working_copy_available(info, sourcename)
return false, e:cat(re)
end
- e2lib.logf(2, "updating %s [%s]", src.working, src.branch)
+ e2lib.logf(2, "updating %s [%s]", src:get_working(), src:get_branch())
- gitwc = e2lib.join(info.root, src.working)
+ gitwc = e2lib.join(info.root, src:get_working())
gitdir = e2lib.join(gitwc, ".git")
argv = generic_git.git_new_argv(gitdir, gitwc, "fetch")
return true
end
- if branch ~= "refs/heads/" .. src.branch then
+ if branch ~= "refs/heads/" .. src:get_branch() then
e2lib.warnf("WOTHER", "not on configured branch. Skipping.")
return true
end
rc, re, remote = generic_git.git_config(
- gitdir, "branch."..src.branch.."remote")
+ gitdir, "branch."..src:get_branch().."remote")
if not rc or string.len(remote) == 0 then
e2lib.warnf("WOTHER", "no remote configured for branch %q. Skipping.",
- src.branch)
+ src:get_branch())
return true
end
- branch = remote .. "/" .. src.branch
+ branch = remote .. "/" .. src:get_branch()
argv = generic_git.git_new_argv(gitdir, gitwc, "merge", "--ff-only", branch)
rc, re = generic_git.git(argv)
if not rc then
function git.fetch_source(info, sourcename)
local e, rc, re, src, git_dir, work_tree, id
- src = info.sources[sourcename]
+ src = source.sources[sourcename]
e = err.new("fetching source failed: %s", sourcename)
- work_tree = e2lib.join(info.root, src.working)
+ work_tree = e2lib.join(info.root, src:get_working())
git_dir = e2lib.join(work_tree, ".git")
- e2lib.logf(2, "cloning %s:%s [%s]", src.server, src.location, src.branch)
+ e2lib.logf(2, "cloning %s:%s [%s]", src:get_server(), src:get_location(),
+ src:get_branch())
- rc, re = generic_git.git_clone_from_server(info.cache, src.server,
- src.location, work_tree, false --[[always checkout]])
+ rc, re = generic_git.git_clone_from_server(info.cache, src:get_server(),
+ src:get_location(), work_tree, false --[[always checkout]])
if not rc then
return false, e:cat(re)
end
rc, re, id = generic_git.lookup_id(git_dir, false,
- "refs/heads/" .. src.branch)
+ "refs/heads/" .. src:get_branch())
if not rc then
return false, e:cat(re)
elseif not id then
- rc, re = generic_git.git_branch_new1(work_tree, true, src.branch,
- "origin/" .. src.branch)
+ rc, re = generic_git.git_branch_new1(work_tree, true, src:get_branch(),
+ "origin/" .. src:get_branch())
if not rc then
return false, e:cat(re)
end
rc, re = generic_git.git_checkout1(work_tree,
- "refs/heads/" .. src.branch)
+ "refs/heads/" .. src:get_branch())
if not rc then
return false, e:cat(re)
end
-- @return bool
-- @return nil on success, an error string on error
function git.prepare_source(info, sourcename, sourceset, buildpath)
- local src = info.sources[ sourcename ]
+ local src = source.sources[sourcename]
local rc, re, e
local e = err.new("preparing git sources failed")
rc, re = scm.generic_source_check(info, sourcename, true)
if not rc then
return false, e:cat(re)
end
- local gitdir = e2lib.join(info.root, src.working, ".git")
+ local gitdir = e2lib.join(info.root, src:get_working(), ".git")
if sourceset == "branch" or
- (sourceset == "lazytag" and src.tag == "^") then
+ (sourceset == "lazytag" and src:get_tag() == "^") then
local argv, work_tree
rc, re = git.git_commit_id(info, sourcename, sourceset)
end
argv = generic_git.git_new_argv(gitdir, work_tree, "checkout", "-f")
- table.insert(argv, "refs/heads/" .. src.branch)
+ table.insert(argv, "refs/heads/" .. src:get_branch())
table.insert(argv, "--")
rc, re = generic_git.git(argv)
return false, e:cat(re)
end
elseif sourceset == "tag" or
- (sourceset == "lazytag" and src.tag ~= "^") then
+ (sourceset == "lazytag" and src:get_tag() ~= "^") then
local argv, work_tree
rc, re = git.git_commit_id(info, sourcename, sourceset)
end
argv = generic_git.git_new_argv(gitdir, work_tree, "checkout", "-f")
- table.insert(argv, "refs/tags/" .. src.tag)
+ table.insert(argv, "refs/tags/" .. src:get_tag())
table.insert(argv, "--")
rc, re = generic_git.git(argv)
elseif sourceset == "working-copy" then
local working, destdir, empty
- working = e2lib.join(info.root, src.working)
+ working = e2lib.join(info.root, src:get_working())
destdir = e2lib.join(buildpath, sourcename)
rc, re = e2lib.mkdir_recursive(destdir)
end
if empty then
- e2lib.warnf("WOTHER", "in result: %s", src.name)
+ e2lib.warnf("WOTHER", "in result: %s", sourcename)
e2lib.warnf("WOTHER", "working copy seems empty")
end
else
return true
end
---- check if a working copy for a git repository is available
+--- Check if a working copy for a git repository is available
-- @param info the info structure
-- @param sourcename string
--- @return bool
--- @return sometimes an error string, when ret. false. XXX interface cleanup.
+-- @return True if available, false otherwise.
function git.working_copy_available(info, sourcename)
- local src = info.sources[sourcename]
- local rc, re
- local e = err.new("checking if working copy is available for source %s",
- sourcename)
- local gitwc = e2lib.join(info.root, src.working)
- local rc = e2lib.isdir(gitwc)
- return rc, nil
+ local rc
+ local src = source.sources[sourcename]
+ local gitwc = e2lib.join(info.root, src:get_working())
+
+ rc = e2lib.isdir(gitwc)
+ return rc
end
function git.has_working_copy(info, sname)
-- @return a table, nil on error
-- @return an error string on failure
function git.display(info, sourcename)
- local src = info.sources[sourcename]
+ error("called git.display")
+ local src = source.sources[sourcename]
local rc, re
local e = err.new("display source information failed")
-- try to calculte the sourceid, but do not care if it fails.
-- working copy might be unavailable
- scm.sourceid(info, sourcename, "tag")
- scm.sourceid(info, sourcename, "branch")
+ src:sourceid("tag")
+ src:sourceid("branch")
local rev_tag = ""
local rev_branch = ""
if src.commitid["tag"] then
rev_branch = string.format("[%s...]", src.commitid["branch"]:sub(1,8))
end
local display = {}
- display[1] = string.format("type = %s", src.type)
- display[2] = string.format("branch = %-15s %s", src.branch, rev_branch)
- display[3] = string.format("tag = %-15s %s", src.tag, rev_tag)
- display[4] = string.format("server = %s", src.server)
- display[5] = string.format("location = %s", src.location)
- display[6] = string.format("working = %s", src.working)
+ display[1] = string.format("type = %s", src:get_type())
+ display[2] = string.format("branch = %-15s %s", src:get_branch(), rev_branch)
+ display[3] = string.format("tag = %-15s %s", src:get_tag(), rev_tag)
+ display[4] = string.format("server = %s", src:get_server())
+ display[5] = string.format("location = %s", src:get_location())
+ display[6] = string.format("working = %s", src:get_working())
local i = 8
for _,l in ipairs(src.licences) do
display[i] = string.format("licence = %s", l)
return display
end
---- calculate an id for a source
--- @param info
--- @param sourcename
--- @param sourceset
--- @return string: the sourceid, or nil
--- @return an error string
-function git.sourceid(info, sourcename, sourceset)
- local src = info.sources[sourcename]
- local rc, re, e, id
- if not src.sourceid then
- src.sourceid = {}
- src.sourceid["working-copy"] = "working-copy"
- src.commitid = {}
- end
- if src.sourceid[sourceset] then
- return true, nil, src.sourceid[sourceset]
- end
-
- rc, re, id = git.git_commit_id(info, sourcename, sourceset,
- e2option.opts["check-remote"])
- if not rc then
- return false, re
- end
-
- src.commitid[sourceset] = id
- local hc = hash.hash_start()
- hash.hash_line(hc, src.name)
- hash.hash_line(hc, src.type)
- hash.hash_line(hc, src._env:id())
- for _,ln in ipairs(src.licences) do
- local lid, re = licence.licences[ln]:licenceid(info)
- if not lid then
- return false, re
- end
- hash.hash_line(hc, lid)
- end
- -- git specific
- --hash.hash_line(hc, src.branch)
- --hash.hash_line(hc, src.tag)
- hash.hash_line(hc, src.server)
- hash.hash_line(hc, src.location)
- hash.hash_line(hc, src.working)
- hash.hash_line(hc, src.commitid[sourceset])
- src.sourceid[sourceset] = hash.hash_finish(hc)
- return true, nil, src.sourceid[sourceset]
-end
-
function git.toresult(info, sourcename, sourceset, directory)
local rc, re, argv
local e = err.new("converting result")
if not rc then
return false, e:cat(re)
end
- local src = info.sources[sourcename]
+ local src = source.sources[sourcename]
local makefile = "Makefile"
local source = "source"
local sourcedir = e2lib.join(directory, source)
- local archive = string.format("%s.tar.gz", src.name)
+ local archive = string.format("%s.tar.gz", src:get_name())
local cmd = nil
rc, re = e2lib.mkdir_recursive(sourcedir)
if sourceset == "tag" or sourceset == "branch" then
local ref, tmpfn
- ref, re = generic_git.sourceset2ref(sourceset, src.branch, src.tag)
+ ref, re = generic_git.sourceset2ref(sourceset, src:get_branch(), src:get_tag())
if not ref then
return false, e:cat(re)
end
return false, e:cat(re)
end
- argv = generic_git.git_new_argv(nil, e2lib.join(info.root, src.working))
+ argv = generic_git.git_new_argv(nil, e2lib.join(info.root, src:get_working()))
table.insert(argv, "archive")
table.insert(argv, "--format=tar") -- older versions don't have "tar.gz"
table.insert(argv, string.format("--prefix=%s/", sourcename))
end
elseif sourceset == "working-copy" then
argv = {
- "-C", e2lib.join(info.root, src.working),
+ "-C", e2lib.join(info.root, src:get_working()),
string.format("--transform=s,^./,./%s/,", sourcename),
"--exclude=.git",
"-czf",
-- write licences
local destdir = e2lib.join(directory, "licences")
local fname = string.format("%s/%s.licences", destdir, archive)
- local licence_list = table.concat(src.licences, "\n") .. "\n"
+ local licences = src:get_licences()
+ local licence_list = licences:concat_sorted("\n") .. "\n"
rc, re = e2lib.mkdir_recursive(destdir)
if not rc then
return false, e:cat(re)
end
-- check if branch exists
- local src = info.sources[sourcename]
- local gitdir = e2lib.join(info.root, src.working, ".git")
- local ref = string.format("refs/heads/%s", src.branch)
+ local src = source.sources[sourcename]
+ local gitdir = e2lib.join(info.root, src:get_working(), ".git")
+ local ref = string.format("refs/heads/%s", src:get_branch())
local id
rc, re, id = generic_git.lookup_id(gitdir, false, ref)
if not rc then
return false, e:cat(re)
elseif not id then
- return false, e:cat(err.new("branch %q does not exist", src.branch))
+ return false, e:cat(err.new("branch %q does not exist", src:get_branch()))
end
-- git config branch.<branch>.remote == "origin"
local query, expect, res
- query = string.format("branch.%s.remote", src.branch)
+ query = string.format("branch.%s.remote", src:get_branch())
res, re = generic_git.git_config(gitdir, query)
if not res then
- e:append("remote is not configured for branch \"%s\"", src.branch)
+ e:append("remote is not configured for branch \"%s\"", src:get_branch())
return false, e
elseif res ~= "origin" then
e:append("%s is not \"origin\"", query)
-- git config remote.origin.url == server:location
query = string.format("remote.origin.url")
- expect, re = git_url(info.cache, src.server, src.location)
+ expect, re = git_url(info.cache, src:get_server(), src:get_location())
if not expect then
return false, e:cat(re)
end
local svn = {}
local cache = require("cache")
+local class = require("class")
local e2lib = require("e2lib")
+local e2tool = require("e2tool")
local eio = require("eio")
local err = require("err")
local hash = require("hash")
local strict = require("strict")
local tools = require("tools")
local url = require("url")
+local source = require("source")
plugin_descriptor = {
description = "SVN SCM Plugin",
- init = function (ctx) scm.register("svn", svn) return true end,
+ init = function (ctx)
+ local rc, re
+
+ rc, re = source.register_source_class("svn", svn.svn_source)
+ if not rc then
+ return false, re
+ end
+
+ rc, re = scm.register("svn", svn)
+ if not rc then
+ return false, re
+ end
+
+ return true
+ end,
exit = function (ctx) return true end,
}
+svn.svn_source = class("svn_source", source.basic_source)
+
--- translate url into subversion url
-- @param u table: url table
-- @return string: subversion style url
return true, nil, table.concat(out)
end
+function svn.svn_source:initialize(rawsrc)
+ assert(type(rawsrc) == "table")
+ assert(type(rawsrc.name) == "string" and #rawsrc.name > 0)
+ assert(type(rawsrc.type) == "string" and rawsrc.type == "svn")
+
+ local rc, re
+
+ source.basic_source.initialize(self, rawsrc)
+
+ self._server = false
+ self._location = false
+ self._tag = false
+ self._branch = false
+ self._working = false
+ self._workingcopy_subdir = false
+ self._sourceids = {
+ ["working-copy"] = "working-copy"
+ }
+
+ rc, re = e2lib.vrfy_dict_exp_keys(rawsrc, "e2source", {
+ "branch",
+ "env",
+ "licences",
+ "location",
+ "name",
+ "server",
+ "tag",
+ "type",
+ "working",
+ "workingcopy_subdir",
+ })
+
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_licences(rawsrc, self)
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_env(rawsrc, self)
+ if not rc then
+ error(re)
+ end
+
+ rc, re = source.generic_source_validate_server(rawsrc, true)
+ if not rc then
+ error(re)
+ end
+ self._server = rawsrc.server
+
+ rc, re = source.generic_source_validate_working(rawsrc)
+ if not rc then
+ error(re)
+ end
+ self._working = rawsrc.working
+
+ -- workingcopy_subdir is optional and defaults to the branch
+ -- make sure branch is checked first to avoid confusing error
+ if rawsrc.workingcopy_subdir == nil then
+ rawsrc.workingcopy_subdir = rawsrc.branch
+ end
+
+ for _,attr in ipairs({"branch", "location", "tag", "workingcopy_subdir"}) do
+ if rawsrc[attr] == nil then
+ error(err.new("source has no `%s' attribute", attr))
+ elseif type(rawsrc[attr]) ~= "string" then
+ error(err.new("'%s' must be a string", attr))
+ elseif rawsrc[attr] == "" then
+ error(err.new("'%s' may not be empty", attr))
+ end
+ end
+
+ self._branch = rawsrc.branch
+ self._location = rawsrc.location
+ self._tag = rawsrc.tag
+ self._workingcopy_subdir = rawsrc.workingcopy_subdir
+end
+
+function svn.svn_source:get_working()
+ assert(type(self._working) == "string")
+ return self._working
+end
+
+function svn.svn_source:get_workingcopy_subdir()
+ assert(type(self._workingcopy_subdir) == "string")
+ return self._workingcopy_subdir
+end
+
+function svn.svn_source:get_server()
+ assert(type(self._server) == "string")
+ return self._server
+end
+
+function svn.svn_source:get_location()
+ assert(type(self._location) == "string")
+ return self._location
+end
+
+function svn.svn_source:get_branch()
+ assert(type(self._branch) == "string")
+ return self._branch
+end
+
+function svn.svn_source:get_tag()
+ assert(type(self._tag) == "string")
+ return self._tag
+end
+
+function svn.svn_source:sourceid(sourceset)
+ assert(type(sourceset) == "string" and #sourceset > 0)
+
+ local rc, re
+ local hc, surl, svnurl, argv, out, svnrev, lid, svnrev, info, licences
+
+ if self._sourceids[sourceset] then
+ return self._sourceids[sourceset]
+ end
+
+ hc = hash.hash_start()
+ hash.hash_line(hc, self._name)
+ hash.hash_line(hc, self._type)
+ hash.hash_line(hc, self._env:id())
+
+ info = e2tool.info()
+ assert(type(info) == "table")
+
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ lid, re = licence.licences[licencename]:licenceid(info)
+ if not lid then
+ return false, re
+ end
+ hash.hash_line(hc, lid)
+ end
+
+ surl, re = cache.remote_url(info.cache, self._server, self._location)
+ if not surl then
+ return false, re
+ end
+
+ svnurl, re = mksvnurl(surl)
+ if not svnurl then
+ return false, re
+ end
+
+ hash.hash_line(hc, self._server)
+ hash.hash_line(hc, self._location)
+
+ if sourceset == "tag" then
+ hash.hash_line(hc, self._tag)
+ argv = { "info", svnurl.."/"..self._tag }
+ elseif sourceset == "branch" then
+ hash.hash_line(hc, self._branch)
+ argv = { "info", svnurl.."/"..self._branch }
+ elseif sourceset == "lazytag" then
+ return false, err.new("svn source does not support lazytag mode")
+ else
+ return false,
+ err.new("svn sourceid can't handle sourceset %q", sourceset)
+ end
+
+ rc, re, out = svn_tool(argv)
+ if not rc then
+ return false,
+ err.new("retrieving revision for tag or branch failed"):cat(re)
+ end
+
+ svnrev = string.match(out, "Last Changed Rev: (%d+)")
+ if not svnrev or string.len(svnrev) == 0 then
+ return false, err.new("could not find SVN revision")
+ end
+ hash.hash_line(hc, svnrev)
+
+ self._sourceids[sourceset] = hash.hash_finish(hc)
+
+ return self._sourceids[sourceset]
+end
+
+function svn.svn_source:display()
+ local d, licences
+
+ -- try to calculte the sourceid, but do not care if it fails.
+ -- working copy might be unavailable
+ self:sourceid("tag")
+ self:sourceid("branch")
+
+ d = {}
+ table.insert(d, string.format("type = %s", self:get_type()))
+ table.insert(d, string.format("server = %s", self._server))
+ table.insert(d, string.format("remote = %s", self._location))
+ table.insert(d, string.format("branch = %s", self._branch))
+ table.insert(d, string.format("tag = %s", self._tag))
+ table.insert(d, string.format("working = %s", self._working))
+
+ licences = self:get_licences()
+ for licencename in licences:iter_sorted() do
+ table.insert(d, string.format("licence = %s", licencename))
+ end
+
+ for sourceset, sid in pairs(self._sourceids) do
+ if sid then
+ table.insert(d, string.format("sourceid [%s] = %s", sourceset, sid))
+ end
+ end
+
+ return d
+end
+
function svn.fetch_source(info, sourcename)
local rc, re
local e = err.new("fetching source failed: %s", sourcename)
- local src = info.sources[sourcename]
- local location = src.location
- local server = src.server
+ local src = source.sources[sourcename]
+ local location = src:get_location()
+ local server = src:get_server()
local surl, re = cache.remote_url(info.cache, server, location)
if not surl then
return false, e:cat(re)
return false, e:cat(re)
end
- local argv = { "checkout", svnurl, info.root .. "/" .. src.working }
+ local argv = { "checkout", svnurl, info.root .. "/" .. src:get_working() }
rc, re = svn_tool(argv)
if not rc then
return false, e:cat(re)
end
- return true, nil
+ return true
end
-function svn.prepare_source(info, sourcename, source_set, build_path)
+function svn.prepare_source(info, sourcename, sourceset, build_path)
local rc, re
local e = err.new("svn.prepare_source failed")
- local src = info.sources[ sourcename ]
- local location = src.location
- local server = src.server
+ local src = source.sources[sourcename]
+ local location = src:get_location()
+ local server = src:get_server()
local surl, re = cache.remote_url(info.cache, server, location)
if not surl then
return false, e:cat(re)
if not svnurl then
return false, e:cat(re)
end
- if source_set == "tag" or source_set == "branch" then
+ if sourceset == "tag" or sourceset == "branch" then
local rev
- if source_set == "tag" then
- rev = src.tag
- else -- source_set == "branch"
- rev = src.branch
+ if sourceset == "tag" then
+ rev = src:get_tag()
+ else -- sourceset == "branch"
+ rev = src:get_branch()
end
local argv = { "export", svnurl .. "/" .. rev,
build_path .. "/" .. sourcename }
if not rc then
return false, e:cat(re)
end
- elseif source_set == "working-copy" then
+ elseif sourceset == "working-copy" then
-- cp -R info.root/src.working/src.workingcopy_subdir build_path
- local s = e2lib.join(info.root, src.working, src.workingcopy_subdir)
- local d = e2lib.join(build_path, src.name)
+ local s = e2lib.join(info.root, src:get_working(),
+ src:get_workingcopy_subdir())
+ local d = e2lib.join(build_path, src:get_name())
rc, re = e2lib.cp(s, d, true)
if not rc then
return false, e:cat(re)
function svn.working_copy_available(info, sourcename)
local rc, re
- local src = info.sources[sourcename]
- local dir = e2lib.join(info.root, src.working)
+ local src = source.sources[sourcename]
+
+ local dir = e2lib.join(info.root, src:get_working())
return e2lib.isdir(dir)
end
local e = err.new("checking working copy failed")
e:append("in source %s (svn configuration):", sourcename)
e:setcount(0)
- local src = info.sources[sourcename]
+ local src = source.sources[sourcename]
if e:getcount() > 0 then
return false, e
end
-- check if the configured branch and tag exist
local d
- d = e2lib.join(info.root, src.working, src.branch)
+ d = e2lib.join(info.root, src:get_working(), src:get_branch())
if not e2lib.isdir(d) then
- e:append("branch does not exist: %s", src.branch)
+ e:append("branch does not exist: %s", src:get_branch())
end
- d = e2lib.join(info.root, src.working, src.tag)
+ d = e2lib.join(info.root, src:get_working(), src:get_tag())
if not e2lib.isdir(d) then
- e:append("tag does not exist: %s", src.tag)
+ e:append("tag does not exist: %s", src:get_tag())
end
if e:getcount() > 0 then
return false, e
end
- return true, nil
+ return true
end
function svn.has_working_copy(info, sname)
return true
end
---- create a table of lines for display
--- @param info the info structure
--- @param sourcename string
--- @return a table, nil on error
--- @return an error string on failure
-function svn.display(info, sourcename)
- local src = info.sources[sourcename]
- local rc, e
- local display = {}
- display[1] = string.format("type = %s", src.type)
- display[2] = string.format("server = %s", src.server)
- display[3] = string.format("remote = %s", src.location)
- display[4] = string.format("branch = %s", src.branch)
- display[5] = string.format("tag = %s", src.tag)
- display[6] = string.format("working = %s", src.working)
- local i = 7
- for _,l in pairs(src.licences) do
- display[i] = string.format("licence = %s", l)
- i = i + 1
- end
- return display
-end
-
---- calculate an id for a source
--- @param info
--- @param sourcename
--- @param source_set
-function svn.sourceid(info, sourcename, source_set)
- local src = info.sources[sourcename]
- local rc, re
- local hc, surl, svnurl, argv, out, svnrev, lid
-
- if not src.sourceid then
- src.sourceid = {}
- end
-
- src.sourceid["working-copy"] = "working-copy"
- if src.sourceid[source_set] then
- return true, nil, src.sourceid[source_set]
- end
-
- hc = hash.hash_start()
- hash.hash_line(hc, src.name)
- hash.hash_line(hc, src.type)
- hash.hash_line(hc, src._env:id())
- for _,ln in pairs(src.licences) do
- lid, re = licence.licences[ln]:licenceid(info)
- if not lid then
- return false, re
- end
- hash.hash_line(hc, lid)
- end
-
- -- svn specific
- surl, re = cache.remote_url(info.cache, src.server, src.location)
- if not surl then
- return false, re
- end
-
- svnurl, re = mksvnurl(surl)
- if not svnurl then
- return false, re
- end
-
- hash.hash_line(hc, src.server)
- hash.hash_line(hc, src.location)
-
- if source_set == "tag" then
- hash.hash_line(hc, src.tag)
- argv = { "info", svnurl.."/"..src.tag }
- elseif source_set == "branch" then
- hash.hash_line(hc, src.branch)
- argv = { "info", svnurl.."/"..src.branch }
- elseif source_set == "lazytag" then
- return false, err.new("svn source does not support lazytag mode")
- else
- return false,
- err.new("svn sourceid can't handle source_set %q", source_set)
- end
-
- rc, re, out = svn_tool(argv)
- if not rc then
- return false,
- err.new("retrieving revision for tag or branch failed"):cat(re)
- end
-
- svnrev = string.match(out, "Last Changed Rev: (%d+)")
- if not svnrev or string.len(svnrev) == 0 then
- return false, err.new("could not find SVN revision")
- end
- hash.hash_line(hc, svnrev)
-
- src.sourceid[source_set] = hash.hash_finish(hc)
-
- return true, nil, src.sourceid[source_set]
-end
-
function svn.toresult(info, sourcename, sourceset, directory)
-- <directory>/source/<sourcename>.tar.gz
-- <directory>/makefile
if not rc then
return false, e:cat(re)
end
- local src = info.sources[sourcename]
+ local src = source.sources[sourcename]
-- write makefile
local makefile = "Makefile"
local source = "source"
return false, e:cat(re)
end
-- create a tarball in the final location
- local archive = string.format("%s.tar.gz", src.name)
+ local archive = string.format("%s.tar.gz", src:get_name())
rc, re = e2lib.tar({ "-C", tmpdir ,"-czf", sourcedir .. "/" .. archive,
sourcename })
if not rc then
-- write licences
local destdir = e2lib.join(directory, "licences")
local fname = string.format("%s/%s.licences", destdir, archive)
- local licence_list = table.concat(src.licences, "\n") .. "\n"
+ local licences = src:get_licences()
+ local licence_list = licences:concat_sorted("\n") .. "\n"
rc, re = e2lib.mkdir_recursive(destdir)
if not rc then
return false, e:cat(re)
function svn.update(info, sourcename)
local rc, re
local e = err.new("updating source '%s' failed", sourcename)
- local src = info.sources[ sourcename ]
- local workdir = e2lib.join(info.root, src.working)
+ local src = source.sources[sourcename]
+ local workdir = e2lib.join(info.root, src:get_working())
rc, re = svn_tool({ "update" }, workdir)
if not rc then
return false, e:cat(re)
return true
end
---- validate source configuration, log errors to the debug log
--- @param info the info table
--- @param sourcename the source name
--- @return bool
-function svn.validate_source(info, sourcename)
- local rc, re = scm.generic_source_validate(info, sourcename)
- if not rc then
- -- error in generic configuration. Don't try to go on.
- return false, re
- end
- local src = info.sources[ sourcename ]
- if not src.sourceid then
- src.sourceid = {}
- end
- local e = err.new("in source %s:", sourcename)
- rc, re = scm.generic_source_default_working(info, sourcename)
- if not rc then
- return false, e:cat(re)
- end
- e:setcount(0)
- if not src.server then
- e:append("source has no `server' attribute")
- end
- if not src.licences then
- e:append("source has no `licences' attribute")
- end
- if not src.location then
- e:append("source has no `location' attribute")
- end
- if src.remote then
- e:append("source has `remote' attribute, not allowed for svn sources")
- end
- if not src.branch then
- e:append("source has no `branch' attribute")
- end
- if type(src.tag) ~= "string" then
- e:append("source has no `tag' attribute or tag attribute has wrong type")
- end
- if type(src.workingcopy_subdir) ~= "string" then
- e2lib.warnf("WDEFAULT", "in source %s", sourcename)
- e2lib.warnf("WDEFAULT",
- " workingcopy_subdir defaults to the branch: %s", src.branch)
- src.workingcopy_subdir = src.branch
- end
- if not src.working then
- e:append("source has no `working' attribute")
- end
- local rc, re = tools.check_tool("svn")
- if not rc then
- e:cat(re)
- end
- if e:getcount() > 0 then
- return false, e
- end
- return true, nil
-end
-
strict.lock(svn)
-- vim:sw=4:sts=4:et: