summaryrefslogtreecommitdiffstatshomepage
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/check_code_size.sh23
-rw-r--r--tools/micropython-upip-1.1.3.tar.gzbin4072 -> 0 bytes
-rwxr-xr-xtools/pip-micropython93
-rwxr-xr-xtools/tinytest-codegen.py9
-rw-r--r--tools/upip.py288
-rw-r--r--tools/upip_utarfile.py94
6 files changed, 409 insertions, 98 deletions
diff --git a/tools/check_code_size.sh b/tools/check_code_size.sh
new file mode 100755
index 0000000000..c5f0c6ffdd
--- /dev/null
+++ b/tools/check_code_size.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# This script check that changes don't lead to code size regressions.
+# (Size of the language core (== minimal port should not grow)).
+#
+
+REFERENCE=$HOME/persist/firmware.bin
+#REFERENCE=/tmp/micropython
+#TRAVIS_PULL_REQUEST=false
+
+if [ -f $REFERENCE ]; then
+ size_old=$(stat -c%s $REFERENCE)
+ size_new=$(stat -c%s minimal/build/firmware.bin)
+ echo "Old size: $size_old new size: $size_new"
+ if [ $size_new -gt $size_old ]; then
+ echo "Validation failure: Core code size increased"
+ if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
+ exit 1
+ fi
+ fi
+else
+ echo "Warning: reference file doesn't exist, code size check didn't run"
+fi
diff --git a/tools/micropython-upip-1.1.3.tar.gz b/tools/micropython-upip-1.1.3.tar.gz
deleted file mode 100644
index 90f726d862..0000000000
--- a/tools/micropython-upip-1.1.3.tar.gz
+++ /dev/null
Binary files differ
diff --git a/tools/pip-micropython b/tools/pip-micropython
deleted file mode 100755
index c7b23f1b6d..0000000000
--- a/tools/pip-micropython
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/bin/sh
-#
-# This tool can be used to install a new package into MicroPython
-# library location (for unix port, default behavior), or produce
-# complete library snapshot to be deployed on a device for baremetal
-# ports (if PIP_MICROPY_DEST environment var is set).
-#
-# Note: this tool is deprecated in favor of "upip" native MicroPython
-# package manager, which is bundled with MicroPython unix binary
-# as a frozen module and can be run as "micropython -u pip" or installed
-# from PyPI package "micropython-upip". This utility is left for
-# reference, regression testing, debugging, etc.
-#
-
-if [ "$1" != "install" ]; then
- echo "Only install command is supported currently"
- exit 1
-fi
-shift
-
-if [ -z "$TMPDIR" ]; then
- TMPDIR=/tmp
-fi
-TMPVENV="$TMPDIR/pip-micropy-venv"
-
-if [ -n "$PIP_MICROPY_DEST" ]; then
- dest="$PIP_MICROPY_DEST"
- echo "Destination snapshot directory: $dest"
-elif [ -n "$MICROPYPATH" ]; then
- libdest=$(echo "$MICROPYPATH" | awk -F: ' {print $1}')
- echo "Destination library directory: $libdest"
-else
- echo "Warning: MICROPYPATH is not set, assuming default value"
- libdest=~/.micropython/lib
- echo "Destination library directory: $libdest"
-fi
-
-# Due to bugs in pip, installation should happen with active virtualenv
-# The issue (at least with pip 1.0 which is still what's shipped with many
-# distros) is that even if --ignore-installed is used, package is not
-# installed if it's already installed for main python distribution.
-if [ ! -d "$TMPVENV" ]; then
- virtualenv --no-site-packages "$TMPVENV"
- # distutils, setuptools, pip are buggy and allow target packages affect
- # their execution environment. For example, if distribution they install
- # has re.py, they will import that instead of system re. So, we need
- # to remove current dir from sys.path, but that appear to be quite uneasy
- # with CPython, so we hook __import__ and exterminate it persistently.
- # See also https://bitbucket.org/pypa/setuptools/issue/187/
- cat > $(ls -1d "$TMPVENV"/lib/python*/)/sitecustomize.py <<EOF
-import sys
-import __builtin__
-old_imp = __import__
-def new_imp(*a, **kw):
- if not sys.path[0]: sys.path.pop(0)
- return old_imp(*a, **kw)
-__builtin__.__import__ = new_imp
-EOF
-fi
-. "$TMPVENV"/bin/activate
-
-# We need to specify --record to override this switch as passed by pip
-# pip will try to parse this file (at the location in specifies), and try to
-# access files as specified in it. But paths there will be relative to --root
-# we pass, so it won't find files and crash. However, if it won't find the
-# file, it will just issue a warning and continue.
-if [ -n "$dest" ]; then
-pip install "$@" \
- --install-option="--install-base=." \
- --install-option="--install-purelib=lib" \
- --install-option="--install-platlib=lib" \
- --install-option="--install-scripts=." \
- --install-option="--install-headers=headers" \
- --install-option="--install-data=lib" \
- --install-option="--record=$TMPDIR/setuptools-record.txt" \
- --install-option="--no-compile" \
- --install-option="--root=$dest"
-else
-# Here we assume that base dir is lib dir, and install scripts a level
-# higher. For default value of ~/.micropython/lib/ , this should give
-# reasonable behavior, though better would make it overridable (or
-# go bold and use ~/bin ?)
-pip install "$@" \
- --install-option="--install-base=." \
- --install-option="--install-purelib=." \
- --install-option="--install-platlib=." \
- --install-option="--install-scripts=.." \
- --install-option="--install-headers=../headers" \
- --install-option="--install-data=." \
- --install-option="--record=$TMPDIR/setuptools-record.txt" \
- --install-option="--no-compile" \
- --install-option="--root=$libdest"
-fi
diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py
index bab937135f..8e505f21a1 100755
--- a/tools/tinytest-codegen.py
+++ b/tools/tinytest-codegen.py
@@ -46,14 +46,13 @@ testgroup_member = (
## XXX: may be we could have `--without <groups>` argument...
# currently these tests are selected because they pass on qemu-arm
-test_dirs = ('basics', 'micropython', 'extmod', 'inlineasm') # 'float', 'import', 'io', 'misc')
+test_dirs = ('basics', 'micropython', 'float', 'extmod', 'inlineasm') # 'import', 'io', 'misc')
exclude_tests = (
+ 'float/float2int_doubleprec.py', # requires double precision floating point to work
'inlineasm/asmfpaddsub.py', 'inlineasm/asmfpcmp.py', 'inlineasm/asmfpldrstr.py', 'inlineasm/asmfpmuldiv.py', 'inlineasm/asmfpsqrt.py',
- 'extmod/time_ms_us.py',
- 'extmod/ujson_dumps_float.py', 'extmod/ujson_loads_float.py',
- 'extmod/uctypes_native_float.py', 'extmod/uctypes_le_float.py',
+ 'extmod/ticks_diff.py', 'extmod/time_ms_us.py',
'extmod/machine_pinbase.py', 'extmod/machine_pulse.py',
- 'extmod/vfs_fat_ramdisk.py',
+ 'extmod/vfs_fat_ramdisk.py', 'extmod/vfs_fat_fileio.py', 'extmod/vfs_fat_fsusermount.py', 'extmod/vfs_fat_oldproto.py',
)
output = []
diff --git a/tools/upip.py b/tools/upip.py
new file mode 100644
index 0000000000..db18a7427e
--- /dev/null
+++ b/tools/upip.py
@@ -0,0 +1,288 @@
+import sys
+import gc
+import uos as os
+import uerrno as errno
+import ujson as json
+import uzlib
+import upip_utarfile as tarfile
+gc.collect()
+
+
+debug = False
+install_path = None
+cleanup_files = []
+gzdict_sz = 16 + 15
+
+file_buf = bytearray(512)
+
+class NotFoundError(Exception):
+ pass
+
+def op_split(path):
+ if path == "":
+ return ("", "")
+ r = path.rsplit("/", 1)
+ if len(r) == 1:
+ return ("", path)
+ head = r[0]
+ if not head:
+ head = "/"
+ return (head, r[1])
+
+def op_basename(path):
+ return op_split(path)[1]
+
+# Expects *file* name
+def _makedirs(name, mode=0o777):
+ ret = False
+ s = ""
+ comps = name.rstrip("/").split("/")[:-1]
+ if comps[0] == "":
+ s = "/"
+ for c in comps:
+ if s and s[-1] != "/":
+ s += "/"
+ s += c
+ try:
+ os.mkdir(s)
+ ret = True
+ except OSError as e:
+ if e.args[0] != errno.EEXIST and e.args[0] != errno.EISDIR:
+ raise
+ ret = False
+ return ret
+
+
+def save_file(fname, subf):
+ global file_buf
+ with open(fname, "wb") as outf:
+ while True:
+ sz = subf.readinto(file_buf)
+ if not sz:
+ break
+ outf.write(file_buf, sz)
+
+def install_tar(f, prefix):
+ meta = {}
+ for info in f:
+ #print(info)
+ fname = info.name
+ try:
+ fname = fname[fname.index("/") + 1:]
+ except ValueError:
+ fname = ""
+
+ save = True
+ for p in ("setup.", "PKG-INFO", "README"):
+ #print(fname, p)
+ if fname.startswith(p) or ".egg-info" in fname:
+ if fname.endswith("/requires.txt"):
+ meta["deps"] = f.extractfile(info).read()
+ save = False
+ if debug:
+ print("Skipping", fname)
+ break
+
+ if save:
+ outfname = prefix + fname
+ if info.type != tarfile.DIRTYPE:
+ if debug:
+ print("Extracting " + outfname)
+ _makedirs(outfname)
+ subf = f.extractfile(info)
+ save_file(outfname, subf)
+ return meta
+
+def expandhome(s):
+ if "~/" in s:
+ h = os.getenv("HOME")
+ s = s.replace("~/", h + "/")
+ return s
+
+import ussl
+import usocket
+warn_ussl = True
+def url_open(url):
+ global warn_ussl
+ proto, _, host, urlpath = url.split('/', 3)
+ ai = usocket.getaddrinfo(host, 443)
+ #print("Address infos:", ai)
+ addr = ai[0][4]
+
+ s = usocket.socket(ai[0][0])
+ #print("Connect address:", addr)
+ s.connect(addr)
+
+ if proto == "https:":
+ s = ussl.wrap_socket(s)
+ if warn_ussl:
+ print("Warning: %s SSL certificate is not validated" % host)
+ warn_ussl = False
+
+ # MicroPython rawsocket module supports file interface directly
+ s.write("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (urlpath, host))
+ l = s.readline()
+ protover, status, msg = l.split(None, 2)
+ if status != b"200":
+ if status == b"404":
+ print("Package not found")
+ raise ValueError(status)
+ while 1:
+ l = s.readline()
+ if not l:
+ raise ValueError("Unexpected EOF")
+ if l == b'\r\n':
+ break
+
+ return s
+
+
+def get_pkg_metadata(name):
+ f = url_open("https://pypi.python.org/pypi/%s/json" % name)
+ s = f.read()
+ f.close()
+ return json.loads(s)
+
+
+def fatal(msg):
+ print(msg)
+ sys.exit(1)
+
+def install_pkg(pkg_spec, install_path):
+ data = get_pkg_metadata(pkg_spec)
+
+ latest_ver = data["info"]["version"]
+ packages = data["releases"][latest_ver]
+ del data
+ gc.collect()
+ assert len(packages) == 1
+ package_url = packages[0]["url"]
+ print("Installing %s %s from %s" % (pkg_spec, latest_ver, package_url))
+ package_fname = op_basename(package_url)
+ f1 = url_open(package_url)
+ f2 = uzlib.DecompIO(f1, gzdict_sz)
+ f3 = tarfile.TarFile(fileobj=f2)
+ meta = install_tar(f3, install_path)
+ f1.close()
+ del f3
+ del f2
+ gc.collect()
+ return meta
+
+def install(to_install, install_path=None):
+ # Calculate gzip dictionary size to use
+ global gzdict_sz
+ sz = gc.mem_free() + gc.mem_alloc()
+ if sz <= 65536:
+ gzdict_sz = 16 + 12
+
+ if install_path is None:
+ install_path = get_install_path()
+ if install_path[-1] != "/":
+ install_path += "/"
+ if not isinstance(to_install, list):
+ to_install = [to_install]
+ print("Installing to: " + install_path)
+ # sets would be perfect here, but don't depend on them
+ installed = []
+ try:
+ while to_install:
+ if debug:
+ print("Queue:", to_install)
+ pkg_spec = to_install.pop(0)
+ if pkg_spec in installed:
+ continue
+ meta = install_pkg(pkg_spec, install_path)
+ installed.append(pkg_spec)
+ if debug:
+ print(meta)
+ deps = meta.get("deps", "").rstrip()
+ if deps:
+ deps = deps.decode("utf-8").split("\n")
+ to_install.extend(deps)
+ except NotFoundError:
+ print("Error: cannot find '%s' package (or server error), packages may be partially installed" \
+ % pkg_spec, file=sys.stderr)
+
+def get_install_path():
+ global install_path
+ if install_path is None:
+ # sys.path[0] is current module's path
+ install_path = sys.path[1]
+ install_path = expandhome(install_path)
+ return install_path
+
+def cleanup():
+ for fname in cleanup_files:
+ try:
+ os.unlink(fname)
+ except OSError:
+ print("Warning: Cannot delete " + fname)
+
+def help():
+ print("""\
+upip - Simple PyPI package manager for MicroPython
+Usage: micropython -m upip install [-p <path>] <package>... | -r <requirements.txt>
+import upip; upip.install(package_or_list, [<path>])
+
+If <path> is not given, packages will be installed into sys.path[1]
+(can be set from MICROPYPATH environment variable, if current system
+supports that).""")
+ print("Current value of sys.path[1]:", sys.path[1])
+ print("""\
+
+Note: only MicroPython packages (usually, named micropython-*) are supported
+for installation, upip does not support arbitrary code in setup.py.
+""")
+
+def main():
+ global debug
+ global install_path
+ install_path = None
+
+ if len(sys.argv) < 2 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
+ help()
+ return
+
+ if sys.argv[1] != "install":
+ fatal("Only 'install' command supported")
+
+ to_install = []
+
+ i = 2
+ while i < len(sys.argv) and sys.argv[i][0] == "-":
+ opt = sys.argv[i]
+ i += 1
+ if opt == "-h" or opt == "--help":
+ help()
+ return
+ elif opt == "-p":
+ install_path = sys.argv[i]
+ i += 1
+ elif opt == "-r":
+ list_file = sys.argv[i]
+ i += 1
+ with open(list_file) as f:
+ while True:
+ l = f.readline()
+ if not l:
+ break
+ to_install.append(l.rstrip())
+ elif opt == "--debug":
+ debug = True
+ else:
+ fatal("Unknown/unsupported option: " + opt)
+
+ to_install.extend(sys.argv[i:])
+ if not to_install:
+ help()
+ return
+
+ install(to_install)
+
+ if not debug:
+ cleanup()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/upip_utarfile.py b/tools/upip_utarfile.py
new file mode 100644
index 0000000000..65ce0bdca8
--- /dev/null
+++ b/tools/upip_utarfile.py
@@ -0,0 +1,94 @@
+import uctypes
+
+# http://www.gnu.org/software/tar/manual/html_node/Standard.html
+TAR_HEADER = {
+ "name": (uctypes.ARRAY | 0, uctypes.UINT8 | 100),
+ "size": (uctypes.ARRAY | 124, uctypes.UINT8 | 12),
+}
+
+DIRTYPE = "dir"
+REGTYPE = "file"
+
+def roundup(val, align):
+ return (val + align - 1) & ~(align - 1)
+
+class FileSection:
+
+ def __init__(self, f, content_len, aligned_len):
+ self.f = f
+ self.content_len = content_len
+ self.align = aligned_len - content_len
+
+ def read(self, sz=65536):
+ if self.content_len == 0:
+ return b""
+ if sz > self.content_len:
+ sz = self.content_len
+ data = self.f.read(sz)
+ sz = len(data)
+ self.content_len -= sz
+ return data
+
+ def readinto(self, buf):
+ if self.content_len == 0:
+ return 0
+ if len(buf) > self.content_len:
+ buf = memoryview(buf)[:self.content_len]
+ sz = self.f.readinto(buf)
+ self.content_len -= sz
+ return sz
+
+ def skip(self):
+ sz = self.content_len + self.align
+ if sz:
+ buf = bytearray(16)
+ while sz:
+ s = min(sz, 16)
+ self.f.readinto(buf, s)
+ sz -= s
+
+class TarInfo:
+
+ def __str__(self):
+ return "TarInfo(%r, %s, %d)" % (self.name, self.type, self.size)
+
+class TarFile:
+
+ def __init__(self, name=None, fileobj=None):
+ if fileobj:
+ self.f = fileobj
+ else:
+ self.f = open(name, "rb")
+ self.subf = None
+
+ def next(self):
+ if self.subf:
+ self.subf.skip()
+ buf = self.f.read(512)
+ if not buf:
+ return None
+
+ h = uctypes.struct(uctypes.addressof(buf), TAR_HEADER, uctypes.LITTLE_ENDIAN)
+
+ # Empty block means end of archive
+ if h.name[0] == 0:
+ return None
+
+ d = TarInfo()
+ d.name = str(h.name, "utf-8").rstrip()
+ d.size = int(bytes(h.size).rstrip(), 8)
+ d.type = [REGTYPE, DIRTYPE][d.name[-1] == "/"]
+ self.subf = d.subf = FileSection(self.f, d.size, roundup(d.size, 512))
+ return d
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ v = self.next()
+ if v is None:
+ raise StopIteration
+ return v
+
+ def extractfile(self, tarinfo):
+ return tarinfo.subf