Skip to content

Commit e25da20

Browse files
authored
Merge pull request #356 from screamerbg/feature_cache
Caching feature
2 parents 1c8ca6c + cf563bc commit e25da20

File tree

3 files changed

+122
-67
lines changed

3 files changed

+122
-67
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ Here is a list of currently implemented configuration settings:
803803
* `ARM_PATH`, `GCC_ARM_PATH`, `IAR_PATH` - defines the default path to ARM Compiler, GCC ARM and IAR Workbench toolchains. Default: none.
804804
* `protocol` - defines the default protocol used for importing or cloning of programs and libraries. The possible values are `https`, `http` and `ssh`. Use `ssh` if you have generated and registered SSH keys (Public Key Authentication) with a service like GitHub, GitLab, Bitbucket, etc. Read more about SSH keys [here](https://help.github.com/articles/generating-an-ssh-key/) Default: `https`.
805805
* `depth` - defines the *clone* depth for importing or cloning and applies only to *Git* repositories. Note that while this option may improve cloning speed, it may also prevent you from correctly checking out a dependency tree when the reference revision hash is older than the clone depth. Read more about shallow clones [here](https://git-scm.com/docs/git-clone). Default: none.
806-
* `cache` (EXPERIMENTAL) - defines the local path that will be used to store minimalistic copies of the imported or cloned repositories, and attempts to use them to minimize traffic and speed up future importing. This feature is still under development, so this should not be used within a production environment. Default: none (disabled).
806+
* `cache` - defines the local path that will be used to store minimalistic copies of the imported or cloned repositories, and attempts to use it to minimize traffic and speed up future imports of the same repositories. Use `on` or `enabled` to turn on caching in the system temp path. Default: none (disabled).
807807

808808
## Troubleshooting
809809

mbed/mbed.py

Lines changed: 120 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@
3333
import urllib2
3434
import zipfile
3535
import argparse
36+
import tempfile
3637

3738

3839
# Application version
39-
ver = '0.9.10'
40+
ver = '1.0.0'
4041

4142
# Default paths to Mercurial and Git
4243
hg_cmd = 'hg'
@@ -121,6 +122,7 @@
121122
verbose = False
122123
very_verbose = False
123124
install_requirements = True
125+
cache_repositories = True
124126

125127
# stores current working directory for recursive operations
126128
cwd_root = ""
@@ -264,10 +266,19 @@ def init(path):
264266
if not os.path.exists(path):
265267
os.mkdir(path)
266268

269+
def cleanup():
270+
info("Cleaning up library build folder")
271+
for fl in os.listdir('.'):
272+
if not fl.startswith('.'):
273+
if os.path.isfile(fl):
274+
os.remove(fl)
275+
else:
276+
shutil.rmtree(fl)
277+
267278
def clone(url, path=None, depth=None, protocol=None):
268279
m = Bld.isurl(url)
269280
if not m:
270-
raise ProcessException(1, "Not an mbed library build URL")
281+
raise ProcessException(1, "Not a library build URL")
271282

272283
try:
273284
Bld.init(path)
@@ -277,60 +288,51 @@ def clone(url, path=None, depth=None, protocol=None):
277288
error(e[1], e[0])
278289

279290
def fetch_rev(url, rev):
280-
tmp_file = os.path.join('.'+Bld.name, '.rev-' + rev + '.zip')
281-
arch_dir = 'mbed-' + rev
291+
rev_file = os.path.join('.'+Bld.name, '.rev-' + rev + '.zip')
282292
try:
283-
if not os.path.exists(tmp_file):
284-
action("Downloading mbed library build \"%s\" (might take a minute)" % rev)
285-
outfd = open(tmp_file, 'wb')
293+
if not os.path.exists(rev_file):
294+
action("Downloading library build \"%s\" (might take a minute)" % rev)
295+
outfd = open(rev_file, 'wb')
286296
inurl = urllib2.urlopen(url)
287297
outfd.write(inurl.read())
288298
outfd.close()
289299
except:
290-
if os.path.isfile(tmp_file):
291-
os.remove(tmp_file)
300+
if os.path.isfile(rev_file):
301+
os.remove(rev_file)
292302
raise Exception(128, "Download failed!\nPlease try again later.")
293303

304+
def unpack_rev(rev):
305+
rev_file = os.path.join('.'+Bld.name, '.rev-' + rev + '.zip')
294306
try:
295-
with zipfile.ZipFile(tmp_file) as zf:
296-
action("Unpacking mbed library build \"%s\" in \"%s\"" % (rev, os.getcwd()))
297-
zf.extractall()
307+
with zipfile.ZipFile(rev_file) as zf:
308+
action("Unpacking library build \"%s\" in \"%s\"" % (rev, os.getcwd()))
309+
zf.extractall('.')
298310
except:
299-
if os.path.isfile(tmp_file):
300-
os.remove(tmp_file)
301-
if os.path.isfile(arch_dir):
302-
rmtree_readonly(arch_dir)
303-
raise Exception(128, "An error occurred while unpacking mbed library archive \"%s\" in \"%s\"" % (tmp_file, os.getcwd()))
311+
if os.path.isfile(rev_file):
312+
os.remove(rev_file)
313+
raise Exception(128, "An error occurred while unpacking library archive \"%s\" in \"%s\"" % (rev_file, os.getcwd()))
304314

305315
def checkout(rev, clean=False):
306316
url = Bld.geturl()
307317
m = Bld.isurl(url)
308318
if not m:
309-
raise ProcessException(1, "Not an mbed library build URL")
319+
raise ProcessException(1, "Not a library build URL")
310320
rev = Hg.remoteid(m.group(1), rev)
311321
if not rev:
312-
error("Unable to fetch late mbed library revision")
313-
314-
if rev != Bld.getrev():
315-
info("Cleaning up library build folder")
316-
for fl in os.listdir('.'):
317-
if not fl.startswith('.'):
318-
if os.path.isfile(fl):
319-
os.remove(fl)
320-
else:
321-
shutil.rmtree(fl)
322+
error("Unable to fetch library build information")
323+
324+
arch_url = m.group(1) + '/archive/' + rev + '.zip'
325+
Bld.fetch_rev(arch_url, rev)
326+
327+
if rev != Bld.getrev() or clean:
328+
Bld.cleanup()
322329

323330
info("Checkout \"%s\" in %s" % (rev, os.path.basename(os.getcwd())))
324-
arch_url = m.group(1) + '/archive/' + rev + '.zip'
325-
arch_dir = m.group(7) + '-' + rev
326331
try:
327-
if not os.path.exists(arch_dir):
328-
Bld.fetch_rev(arch_url, rev)
332+
Bld.unpack_rev(rev)
333+
Bld.seturl(url+'/'+rev)
329334
except Exception as e:
330-
if os.path.exists(arch_dir):
331-
rmtree_readonly(arch_dir)
332335
error(e[1], e[0])
333-
Bld.seturl(url+'/'+rev)
334336

335337
def update(rev=None, clean=False, clean_files=False, is_local=False):
336338
return Bld.checkout(rev, clean)
@@ -339,6 +341,7 @@ def untracked():
339341
return ""
340342

341343
def seturl(url):
344+
info("Setting url to \"%s\" in %s" % (url, os.getcwd()))
342345
if not os.path.exists('.'+Bld.name):
343346
os.mkdir('.'+Bld.name)
344347

@@ -382,6 +385,9 @@ def isurl(url):
382385
def init(path=None):
383386
popen([hg_cmd, 'init'] + ([path] if path else []) + (['-v'] if very_verbose else ([] if verbose else ['-q'])))
384387

388+
def cleanup():
389+
return True
390+
385391
def clone(url, name=None, depth=None, protocol=None):
386392
popen([hg_cmd, 'clone', formaturl(url, protocol), name] + (['-v'] if very_verbose else ([] if verbose else ['-q'])))
387393

@@ -445,11 +451,36 @@ def outgoing():
445451
raise e
446452
return 0
447453

454+
def seturl(url):
455+
info("Setting url to \"%s\" in %s" % (url, os.getcwd()))
456+
hgrc = os.path.join('.hg', 'hgrc')
457+
tagpaths = '[paths]'
458+
remote = 'default'
459+
lines = []
460+
461+
try:
462+
with open(hgrc) as f:
463+
lines = f.read().splitlines()
464+
except IOError:
465+
pass
466+
467+
if tagpaths in lines:
468+
idx = lines.index(tagpaths)
469+
m = re.match(r'^([\w_]+)\s*=\s*(.*)$', lines[idx+1])
470+
if m:
471+
remote = m.group(1)
472+
del lines[idx+1]
473+
lines.insert(idx, remote+' = '+url)
474+
else:
475+
lines.append(tagpaths)
476+
lines.append(remote+' = '+url)
477+
448478
def geturl():
449479
tagpaths = '[paths]'
450480
default_url = ''
451481
url = ''
452-
if os.path.isfile(os.path.join('.hg', 'hgrc')):
482+
483+
try:
453484
with open(os.path.join('.hg', 'hgrc')) as f:
454485
lines = f.read().splitlines()
455486
if tagpaths in lines:
@@ -460,8 +491,11 @@ def geturl():
460491
default_url = m.group(2)
461492
else:
462493
url = m.group(2)
463-
if default_url:
464-
url = default_url
494+
except IOError:
495+
pass
496+
497+
if default_url:
498+
url = default_url
465499

466500
return formaturl(url or pquery([hg_cmd, 'paths', 'default']).strip())
467501

@@ -551,6 +585,11 @@ def isurl(url):
551585
def init(path=None):
552586
popen([git_cmd, 'init'] + ([path] if path else []) + ([] if very_verbose else ['-q']))
553587

588+
def cleanup():
589+
info("Cleaning up Git index")
590+
if os.path.exists(os.path.join('.git', 'logs')):
591+
rmtree_readonly(os.path.join('.git', 'logs'))
592+
554593
def clone(url, name=None, depth=None, protocol=None):
555594
popen([git_cmd, 'clone', formaturl(url, protocol), name] + (['--depth', depth] if depth else []) + (['-v'] if very_verbose else ([] if verbose else ['-q'])))
556595

@@ -623,10 +662,10 @@ def checkout(rev, clean=False):
623662
popen([git_cmd, 'checkout', rev] + (['-f'] if clean else []) + ([] if very_verbose else ['-q']))
624663

625664
def update(rev=None, clean=False, clean_files=False, is_local=False):
626-
if clean:
627-
Git.discard(clean_files)
628665
if not is_local:
629666
Git.fetch()
667+
if clean:
668+
Git.discard(clean_files)
630669
if rev:
631670
Git.checkout(rev, clean)
632671
else:
@@ -638,11 +677,11 @@ def update(rev=None, clean=False, clean_files=False, is_local=False):
638677
except ProcessException:
639678
pass
640679
else:
641-
err = "Unable to update \"%s\" in \"%s\".\n" % (os.path.basename(os.getcwd()), os.getcwd())
680+
err = "Unable to update \"%s\" in \"%s\"." % (os.path.basename(os.getcwd()), os.getcwd())
642681
if not remote:
643-
info(err+"The local repository is not associated with a remote one.")
682+
info(err+" The local repository is not associated with a remote one.")
644683
if not branch:
645-
info(err+"Working set is not on a branch.")
684+
info(err+" Working set is not on a branch.")
646685

647686
def status():
648687
return pquery([git_cmd, 'status', '-s'] + (['-v'] if very_verbose else []))
@@ -698,6 +737,10 @@ def getremotes(rtype='fetch'):
698737
result.append([remote[0], remote[1], t])
699738
return result
700739

740+
def seturl(url):
741+
info("Setting url to \"%s\" in %s" % (url, os.getcwd()))
742+
return pquery([git_cmd, 'remote', 'set-url', 'origin', url]).strip()
743+
701744
def geturl():
702745
url = ""
703746
remotes = Git.getremotes()
@@ -810,12 +853,15 @@ def fromurl(cls, url, path=None):
810853
repo.path = os.path.abspath(path or os.path.join(os.getcwd(), repo.name))
811854
repo.url = formaturl(m_repo_url.group(1))
812855
repo.rev = m_repo_url.group(3)
813-
if repo.rev and not re.match(r'^([a-fA-F0-9]{6,40})$', repo.rev):
856+
if repo.rev and repo.rev != 'latest' and not re.match(r'^([a-fA-F0-9]{6,40})$', repo.rev):
814857
error('Invalid revision (%s)' % repo.rev, -1)
815858
else:
816859
error('Invalid repository (%s)' % url.strip(), -1)
817860

818-
repo.cache = Program(repo.path).get_cfg('CACHE')
861+
cache_cfg = Global().get_cfg('CACHE', '')
862+
if cache_repositories and cache_cfg and cache_cfg != 'none' and cache_cfg != 'off' and cache_cfg != 'disabled':
863+
loc = cache_cfg if (cache_cfg and cache_cfg != 'on' and cache_cfg != 'enabled') else None
864+
repo.cache = loc or os.path.join(tempfile.gettempdir(), 'mbed-repo-cache')
819865

820866
return repo
821867

@@ -846,7 +892,11 @@ def fromrepo(cls, path=None):
846892

847893
repo.path = os.path.abspath(path)
848894
repo.name = os.path.basename(repo.path)
849-
repo.cache = Program(repo.path).get_cfg('CACHE')
895+
896+
cache_cfg = Global().get_cfg('CACHE', '')
897+
if cache_repositories and cache_cfg and cache_cfg != 'none' and cache_cfg != 'off' and cache_cfg != 'disabled':
898+
loc = cache_cfg if (cache_cfg and cache_cfg != 'on' and cache_cfg != 'enabled') else None
899+
repo.cache = loc or os.path.join(tempfile.gettempdir(), 'mbed-repo-cache')
850900

851901
repo.sync()
852902

@@ -1004,10 +1054,13 @@ def clone(self, url, path, rev=None, depth=None, protocol=None, **kwargs):
10041054
shutil.copytree(cache, path)
10051055

10061056
with cd(path):
1057+
scm.seturl(formaturl(url, protocol))
1058+
scm.cleanup()
10071059
info("Update cached copy from remote repository")
10081060
scm.update(rev, True)
10091061
main = False
10101062
except (ProcessException, IOError):
1063+
info("Discarding cached repository")
10111064
if os.path.isdir(path):
10121065
rmtree_readonly(path)
10131066

@@ -1160,7 +1213,7 @@ def __init__(self, path=None, print_warning=False):
11601213
if self.is_cwd and print_warning:
11611214
warning(
11621215
"Could not find mbed program in current path \"%s\".\n"
1163-
"You can fix this by calling \"mbed new .\" or \"mbed config root .\" in the root of your program." % self.path)
1216+
"You can fix this by calling \"mbed new .\" in the root of your program." % self.path)
11641217

11651218
def get_cfg(self, *args, **kwargs):
11661219
return Cfg(self.path).get(*args, **kwargs) or Global().get_cfg(*args, **kwargs)
@@ -1304,18 +1357,15 @@ def add_tools(self, path):
13041357
error("An error occurred while cloning the mbed SDK tools from \"%s\"" % mbed_sdk_tools_url)
13051358

13061359
def update_tools(self, path):
1307-
if not os.path.exists(path):
1308-
os.mkdir(path)
1309-
with cd(path):
1310-
tools_dir = 'tools'
1311-
if os.path.exists(tools_dir):
1312-
with cd(tools_dir):
1313-
try:
1314-
action("Updating the mbed 2.0 SDK tools...")
1315-
repo = Repo.fromrepo()
1316-
repo.update()
1317-
except Exception:
1318-
error("An error occurred while update the mbed SDK tools from \"%s\"" % mbed_sdk_tools_url)
1360+
tools_dir = 'tools'
1361+
if os.path.exists(os.path.join(path, tools_dir)):
1362+
with cd(os.path.join(path, tools_dir)):
1363+
try:
1364+
action("Updating the mbed 2.0 SDK tools...")
1365+
repo = Repo.fromrepo()
1366+
repo.update()
1367+
except Exception:
1368+
error("An error occurred while update the mbed SDK tools from \"%s\"" % mbed_sdk_tools_url)
13191369

13201370
def get_tools(self):
13211371
mbed_tools_path = self.get_tools_dir()
@@ -1373,6 +1423,7 @@ def get_macros(self):
13731423
macros = f.read().splitlines()
13741424
return macros
13751425

1426+
13761427
def ignore_build_dir(self):
13771428
build_path = os.path.join(self.path, self.build_dir)
13781429
if not os.path.exists(build_path):
@@ -1436,6 +1487,9 @@ def __init__(self, path):
14361487

14371488
# Sets config value
14381489
def set(self, var, val):
1490+
if not re.match(r'^([\w+-]+)$', var):
1491+
error("%s is invalid config variable name" % var)
1492+
14391493
fl = os.path.join(self.path, self.file)
14401494
try:
14411495
with open(fl) as f:
@@ -1630,15 +1684,11 @@ def new(name, scm='git', program=False, library=False, mbedlib=False, create_onl
16301684
p.path = cwd_root
16311685
p.set_root()
16321686
if not create_only and not p.get_os_dir() and not p.get_mbedlib_dir():
1633-
url = mbed_lib_url if mbedlib else mbed_os_url
1687+
url = mbed_lib_url if mbedlib else mbed_os_url+'#latest'
16341688
d = 'mbed' if mbedlib else 'mbed-os'
16351689
try:
16361690
with cd(d_path):
16371691
add(url, depth=depth, protocol=protocol, top=False)
1638-
if not mbedlib:
1639-
with cd(d):
1640-
repo = Repo.fromrepo()
1641-
repo.checkout('latest')
16421692
except Exception as e:
16431693
if os.path.isdir(os.path.join(d_path, d)):
16441694
rmtree_readonly(os.path.join(d_path, d))
@@ -1690,7 +1740,8 @@ def import_(url, path=None, ignore=False, depth=None, protocol=None, top=True):
16901740
with cd(repo.path):
16911741
Program(repo.path).set_root()
16921742
try:
1693-
repo.checkout(repo.rev, True)
1743+
if repo.rev and repo.getrev() != repo.rev:
1744+
repo.checkout(repo.rev, True)
16941745
except ProcessException as e:
16951746
err = "Unable to update \"%s\" to %s" % (repo.name, repo.revtype(repo.rev, True))
16961747
if depth:
@@ -2383,6 +2434,10 @@ def config_(var=None, value=None, global_cfg=False, unset=False, list_config=Fal
23832434
else:
23842435
# Find the root of the program
23852436
program = Program(os.getcwd())
2437+
if program.is_cwd:
2438+
error(
2439+
"Could not find mbed program in current path \"%s\".\n"
2440+
"Change the current directory to a valid mbed program or use the '--global' option to set global configuration." % program.path)
23862441
with cd(program.path):
23872442
if unset:
23882443
program.set_cfg(var, None)

0 commit comments

Comments
 (0)