Skip to content

Commit 4a04369

Browse files
denniskleindberzano
authored andcommitted
Only clone reference repos by default
Force fetch updates with --fetch-repos or -u. This new sensible default enables aliBuild to be used with no or limited network connectivity.
1 parent 6b219cc commit 4a04369

File tree

7 files changed

+123
-79
lines changed

7 files changed

+123
-79
lines changed

aliBuild

+1-3
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ def doMain(args, parser):
7575

7676
# Setup build environment.
7777
if args.action == "init":
78-
doInit(setdir=args.develPrefix, configDir=args.configDir,
79-
pkgname=args.pkgname, referenceSources=args.referenceSources,
80-
dist=args.dist, defaults=args.defaults, dryRun=args.dryRun)
78+
doInit(args)
8179
exit(0)
8280

8381
if args.action == "build":

alibuild_helpers/args.py

+4
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ def doParseArgs(star):
108108
dest="autoCleanup", action="store_false", default=True)
109109
build_parser.add_argument("--devel-prefix", "-z", nargs="?", help="Version name to use for development packages. Defaults to branch name.",
110110
dest="develPrefix", default=argparse.SUPPRESS)
111+
build_parser.add_argument("--fetch-repos", "-u", dest="fetchRepos", default=False,
112+
action="store_true", help="Fetch repository updates")
111113

112114
group = build_parser.add_mutually_exclusive_group()
113115
group.add_argument("--always-prefer-system", dest="preferSystem", default=False,
@@ -151,6 +153,8 @@ def doParseArgs(star):
151153
metavar="FILE", help="Specify which defaults to use")
152154
init_parser.add_argument("--chdir", "-C", help="Change to the specified directory first",
153155
metavar="DIR", dest="chdir", default=DEFAULT_CHDIR)
156+
init_parser.add_argument("--fetch-repos", "-u", dest="fetchRepos", default=False,
157+
action="store_true", help="Fetch repository updates")
154158

155159
# Options for the version subcommand
156160
version_parser.add_argument("--architecture", "-a", dest="architecture",

alibuild_helpers/build.py

+13-28
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from alibuild_helpers.log import logger_handler, LogFormatter, ProgressPrint, riemannStream
2323
from datetime import datetime
2424
from glob import glob
25-
from multiprocessing.pool import ThreadPool
2625
try:
2726
from collections import OrderedDict
2827
except ImportError:
@@ -344,33 +343,22 @@ def doBuild(args, parser):
344343
" git pull --rebase\n",
345344
pwd=os.getcwd(), star=star()))
346345

347-
# Prefetch git heads in parallel
348-
debug("Prefetching git heads in parallel:")
346+
# Clone/update repos
347+
for p in [p for p in buildOrder if "source" in specs[p]]:
348+
if not args.fetchRepos:
349+
specs[p]["reference"] = join(abspath(args.referenceSources), p.lower())
350+
if args.fetchRepos or not exists(specs[p]["reference"]):
351+
updateReferenceRepos(args.referenceSources, p, specs[p])
349352

350-
fetchers = list(filter(lambda p: "source" in specs[p], buildOrder))
351-
poolConfig = {"processes": min(max(len(fetchers), 1), 16)}
352-
pool = ThreadPool(**poolConfig)
353-
debug("Created thread pool with config: %s" % poolConfig)
354-
355-
git_commands = {} # store futures
356-
for p in fetchers:
357-
# Replace source with local one if we are in development mode.
353+
# Retrieve git heads
354+
for p in [p for p in buildOrder if "reference" in specs[p]]:
355+
cmd = "git ls-remote --heads %s" % specs[p]["reference"]
358356
if specs[p]["package"] in develPkgs:
359357
specs[p]["source"] = join(os.getcwd(), specs[p]["package"])
360-
361-
cmd = "git ls-remote --heads %s" % specs[p]["source"]
362-
debug("Enqueued '%s'" % cmd)
363-
git_commands[p] = pool.apply_async(getstatusoutput, (cmd,))
364-
365-
results = {}
366-
for p, future in git_commands.items():
367-
results[p] = future.get()
368-
369-
for p, result in results.items():
370-
dieOnError(result[0], "Unable to fetch from %s" % specs[p]["source"])
371-
specs[p]["git_heads"] = result[1].split("\n")
372-
373-
debug("Finished prefetching git heads")
358+
cmd = "git ls-remote --heads %s" % specs[p]["source"]
359+
res, output = getStatusOutputBash(cmd)
360+
dieOnError(res, "Error on '%s': %s" % (cmd, output))
361+
specs[p]["git_heads"] = output.split("\n")
374362

375363
# Resolve the tag to the actual commit ref, so that
376364
for p in buildOrder:
@@ -771,9 +759,6 @@ def doBuild(args, parser):
771759
"Found tarball in %s" % spec["cachedTarball"] or
772760
"No cache tarballs found")
773761

774-
# FIXME: Why doing it here? This should really be done before anything else.
775-
updateReferenceRepos(args.referenceSources, p, spec)
776-
777762
# Generate the part which sources the environment for all the dependencies.
778763
# Notice that we guarantee that a dependency is always sourced before the
779764
# parts depending on it, but we do not guaranteed anything for the order in

alibuild_helpers/init.py

+30-24
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from alibuild_helpers.log import dieOnError
66
from alibuild_helpers.workarea import updateReferenceRepos
77

8-
from os.path import basename, join
8+
from os.path import abspath, basename, join
99
import os.path as path
1010
import os
1111
try:
@@ -17,83 +17,89 @@ def parsePackagesDefinition(pkgname):
1717
return [ dict(zip(["name","ver"], y.split("@")[0:2]))
1818
for y in [ x+"@" for x in list(filter(lambda y: y, pkgname.split(","))) ] ]
1919

20-
def doInit(setdir, configDir, pkgname, referenceSources, dist, defaults, dryRun):
21-
assert(pkgname != None)
22-
assert(type(dist) == dict)
23-
assert(sorted(dist.keys()) == ["repo", "ver"])
24-
pkgs = parsePackagesDefinition(pkgname)
20+
def doInit(args):
21+
assert(args.pkgname != None)
22+
assert(type(args.dist) == dict)
23+
assert(sorted(args.dist.keys()) == ["repo", "ver"])
24+
pkgs = parsePackagesDefinition(args.pkgname)
2525
assert(type(pkgs) == list)
26-
if dryRun:
26+
if args.dryRun:
2727
info("This will initialise local checkouts for %s\n"
2828
"--dry-run / -n specified. Doing nothing." % ",".join(x["name"] for x in pkgs))
2929
exit(0)
3030
try:
31-
path.exists(setdir) or os.mkdir(setdir)
32-
path.exists(referenceSources) or os.makedirs(referenceSources)
31+
path.exists(args.develPrefix) or os.mkdir(args.develPrefix)
32+
path.exists(args.referenceSources) or os.makedirs(args.referenceSources)
3333
except OSError as e:
3434
error(str(e))
3535
exit(1)
3636

3737
# Fetch recipes first if necessary
38-
if path.exists(configDir):
39-
warning("using existing recipes from %s" % configDir)
38+
if path.exists(args.configDir):
39+
warning("using existing recipes from %s" % args.configDir)
4040
else:
4141
cmd = format("git clone %(repo)s%(branch)s %(cd)s",
42-
repo=dist["repo"] if ":" in dist["repo"] else "https://github.com/%s" % dist["repo"],
43-
branch=" -b "+dist["ver"] if dist["ver"] else "",
44-
cd=configDir)
42+
repo=args.dist["repo"] if ":" in args.dist["repo"] else "https://github.com/%s" % args.dist["repo"],
43+
branch=" -b "+args.dist["ver"] if args.dist["ver"] else "",
44+
cd=args.configDir)
4545
debug(cmd)
4646
err = execute(cmd)
4747
dieOnError(err!=0, "cannot clone recipes")
4848

4949
# Use standard functions supporting overrides and taps. Ignore all disables
5050
# and system packages as they are irrelevant in this context
5151
specs = {}
52-
defaultsReader = lambda: readDefaults(configDir, defaults, error)
52+
defaultsReader = lambda: readDefaults(args.configDir, args.defaults, error)
5353
(err, overrides, taps) = parseDefaults([], defaultsReader, debug)
5454
(_,_,_,validDefaults) = getPackageList(packages=[ p["name"] for p in pkgs ],
5555
specs=specs,
56-
configDir=configDir,
56+
configDir=args.configDir,
5757
preferSystem=False,
5858
noSystem=True,
5959
architecture="",
6060
disable=[],
61-
defaults=defaults,
61+
defaults=args.defaults,
6262
dieOnError=lambda *x, **y: None,
6363
performPreferCheck=lambda *x, **y: (1, ""),
6464
performRequirementCheck=lambda *x, **y: (0, ""),
65-
performValidateDefaults=lambda spec : validateDefaults(spec, defaults),
65+
performValidateDefaults=lambda spec : validateDefaults(spec, args.defaults),
6666
overrides=overrides,
6767
taps=taps,
6868
log=debug)
69-
dieOnError(validDefaults and defaults not in validDefaults,
70-
"Specified default `%s' is not compatible with the packages you want to build.\n" % defaults +
69+
dieOnError(validDefaults and args.defaults not in validDefaults,
70+
"Specified default `%s' is not compatible with the packages you want to build.\n" % args.defaults +
7171
"Valid defaults:\n\n- " +
7272
"\n- ".join(sorted(validDefaults)))
7373

7474
for p in pkgs:
7575
spec = specs.get(p["name"])
7676
dieOnError(spec is None, "cannot find recipe for package %s" % p["name"])
77-
dest = join(setdir, spec["package"])
77+
dest = join(args.develPrefix, spec["package"])
7878
writeRepo = spec.get("write_repo", spec.get("source"))
7979
dieOnError(not writeRepo, "package %s has no source field and cannot be developed" % spec["package"])
8080
if path.exists(dest):
8181
warning("not cloning %s since it already exists" % spec["package"])
8282
continue
8383
p["ver"] = p["ver"] if p["ver"] else spec.get("tag", spec["version"])
8484
debug("cloning %s%s for development" % (spec["package"], " version "+p["ver"] if p["ver"] else ""))
85-
updateReferenceRepos(referenceSources, spec["package"], spec)
85+
86+
if not args.fetchRepos:
87+
spec["reference"] = path.join(abspath(args.referenceSources), spec["package"].lower())
88+
89+
if args.fetchRepos or not path.exists(spec["reference"]):
90+
updateReferenceRepos(args.referenceSources, spec["package"], spec)
91+
8692
cmd = format("git clone %(readRepo)s%(branch)s --reference %(refSource)s %(cd)s && " +
8793
"cd %(cd)s && git remote set-url --push origin %(writeRepo)s",
8894
readRepo=spec["source"],
8995
writeRepo=writeRepo,
9096
branch=" -b "+p["ver"] if p["ver"] else "",
91-
refSource=join(referenceSources, spec["package"].lower()),
97+
refSource=join(args.referenceSources, spec["package"].lower()),
9298
cd=dest)
9399
debug(cmd)
94100
err = execute(cmd)
95101
dieOnError(err!=0, "cannot clone %s%s" %
96102
(spec["package"], " version "+p["ver"] if p["ver"] else ""))
97103
banner(format("Development directory %(d)s created%(pkgs)s",
98104
pkgs=" for "+", ".join([ x["name"].lower() for x in pkgs ]) if pkgs else "",
99-
d=setdir))
105+
d=args.develPrefix))

docs/user.markdown

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ For a quick start introduction, please look [here](./quick.html).
1717
[--always-prefer-system] [--no-system]
1818
[--force-unknown-architecture] [--insecure]
1919
[--aggressive-cleanup] [--debug] [--no-auto-cleanup]
20-
[--devel-prefix [DEVELPREFIX]] [--dist DIST] [--dry-run]
20+
[--devel-prefix [DEVELPREFIX]] [--dist DIST]
21+
[--dry-run] [--fetch-repos|-u]
2122
{init,build,clean} [pkgname]
2223

2324
positional arguments:
@@ -62,6 +63,7 @@ For a quick start introduction, please look [here](./quick.html).
6263
recipes set ([user/repo@]branch)
6364
--dry-run, -n Prints what would happen, without actually doing the
6465
build.
66+
--fetch-repos, -u Fetch repository updates
6567

6668

6769
## Speedup build process by using a build store

tests/test_build.py

+31-6
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,13 @@ def dummy_getstatusoutput(x):
8787
"GIT_DIR=/alidist/.git git rev-parse HEAD": (0, "6cec7b7b3769826219dfa85e5daa6de6522229a0"),
8888
'pip --disable-pip-version-check show alibuild | grep -e "^Version:" | sed -e \'s/.* //\'': (0, "v1.5.0"),
8989
'which pigz': (1, ""),
90-
'tar --ignore-failed-read -cvvf /dev/null /dev/zero': (0, ""),
91-
'git ls-remote --heads https://github.com/root-mirror/root': (0, "87b87c4322d2a3fad315c919cb2e2dd73f2154dc\trefs/heads/master\nf7b336611753f1f4aaa94222b0d620748ae230c0\trefs/heads/v6-08-00-patches"),
92-
'git ls-remote --heads https://github.com/star-externals/zlib': (0, "8822efa61f2a385e0bc83ca5819d608111b2168a\trefs/heads/master")
90+
'tar --ignore-failed-read -cvvf /dev/null /dev/zero': (0, "")
91+
}[x]
92+
93+
def dummy_getStatusOutputBash(x):
94+
return {
95+
'git ls-remote --heads /sw/MIRROR/root': (0, "87b87c4322d2a3fad315c919cb2e2dd73f2154dc\trefs/heads/master\nf7b336611753f1f4aaa94222b0d620748ae230c0\trefs/heads/v6-08-00-patches"),
96+
'git ls-remote --heads /sw/MIRROR/zlib': (0, "8822efa61f2a385e0bc83ca5819d608111b2168a\trefs/heads/master")
9397
}[x]
9498

9599
TIMES_ASKED = {}
@@ -127,7 +131,17 @@ def dummy_readlink(x):
127131
def dummy_exists(x):
128132
if x.endswith("alibuild_helpers/.git"):
129133
return False
130-
return {"/alidist": True, "/sw/SPECS": False, "/alidist/.git": True, "alibuild_helpers/.git": False}[x]
134+
return {
135+
"/alidist": True,
136+
"/sw/SPECS": False,
137+
"/alidist/.git": True,
138+
"alibuild_helpers/.git": False,
139+
"/sw/MIRROR/root": True,
140+
"/sw/MIRROR/zlib": False}[x]
141+
142+
def dummy_reference(referenceSources, p, spec):
143+
spec["reference"] = os.path.join(os.path.abspath(referenceSources), p.lower())
144+
131145

132146
# A few errors we should handle, together with the expected result
133147
class BuildTestCase(unittest.TestCase):
@@ -147,7 +161,8 @@ class BuildTestCase(unittest.TestCase):
147161
@patch("alibuild_helpers.build.glob")
148162
@patch("alibuild_helpers.build.readlink")
149163
@patch("alibuild_helpers.build.banner")
150-
def test_coverDoBuild(self, mock_banner, mock_readlink, mock_glob, mock_shutil, mock_open, mock_utilities_open, mock_reference, mock_debug, mock_makedirs, mock_read_defaults, mock_die, mock_sys, mock_exists, mock_getstatusoutput, mock_execute, mock_urlopen):
164+
@patch("alibuild_helpers.build.getStatusOutputBash")
165+
def test_coverDoBuild(self, mock_getStatusOutputBash, mock_banner, mock_readlink, mock_glob, mock_shutil, mock_open, mock_utilities_open, mock_reference, mock_debug, mock_makedirs, mock_read_defaults, mock_die, mock_sys, mock_exists, mock_getstatusoutput, mock_execute, mock_urlopen):
151166
mock_readlink.side_effect = dummy_readlink
152167
mock_glob.side_effect = lambda x : {"*": ["zlib"],
153168
"/sw/TARS/osx_x86-64/defaults-release/defaults-release-v1-*.osx_x86-64.tar.gz": ["/sw/TARS/osx_x86-64/defaults-release/defaults-release-v1-1.osx_x86-64.tar.gz"],
@@ -168,6 +183,7 @@ def test_coverDoBuild(self, mock_banner, mock_readlink, mock_glob, mock_shutil,
168183
mock_execute.side_effect = dummy_execute
169184
mock_exists.side_effect = dummy_exists
170185
mock_getstatusoutput.side_effect = dummy_getstatusoutput
186+
mock_getStatusOutputBash.side_effect = dummy_getStatusOutputBash
171187
mock_parser = MagicMock()
172188
mock_read_defaults.return_value = (OrderedDict({"package": "defaults-release", "disable": []}), "")
173189
args = Namespace(
@@ -189,12 +205,21 @@ def test_coverDoBuild(self, mock_banner, mock_readlink, mock_glob, mock_shutil,
189205
aggressiveCleanup=False,
190206
environment={},
191207
autoCleanup=False,
192-
noDevel=[]
208+
noDevel=[],
209+
fetchRepos=False
193210
)
211+
mock_reference.side_effect = dummy_reference
194212
mock_sys.version_info = sys.version_info
195213
fmt, msg, code = doBuild(args, mock_parser)
214+
self.assertEqual(mock_reference.call_count, 1, "Only one repo should have been fetched")
215+
216+
# Force fetching repos
217+
mock_reference.reset_mock()
218+
args.fetchRepos = True
219+
fmt, msg, code = doBuild(args, mock_parser)
196220
mock_glob.assert_called_with("/sw/TARS/osx_x86-64/ROOT/ROOT-v6-08-30-*.osx_x86-64.tar.gz")
197221
self.assertEqual(msg, "Everything done")
222+
self.assertEqual(mock_reference.call_count, 2, "Both repos should have been fetched")
198223

199224
@patch("alibuild_helpers.build.urlopen")
200225
@patch("alibuild_helpers.build.execute")

0 commit comments

Comments
 (0)