forked from M-Labs/artiq
1
0
Fork 0

Merge branch 'master' into new-py2llvm

This commit is contained in:
whitequark 2015-10-30 04:29:35 +03:00
commit f70f7fb89b
114 changed files with 1112 additions and 1158 deletions

3
.gitignore vendored
View File

@ -13,6 +13,9 @@ doc/manual/_build
/*.egg-info
/.coverage
artiq/test/results
artiq/test/h5types.h5
examples/master/results
examples/master/dataset_db.pyon
examples/sim/dataset_db.pyon
Output/
/lit-test/libartiq_support/libartiq_support.so

View File

@ -9,29 +9,40 @@ env:
global:
- secure: "DUk/Ihg8KbbzEgPF0qrHqlxU8e8eET9i/BtzNvFddIGX4HP/P2qz0nk3cVkmjuWhqJXSbC22RdKME9qqPzw6fJwJ6dpJ3OR6dDmSd7rewavq+niwxu52PVa+yK8mL4yf1terM7QQ5tIRf+yUL9qGKrZ2xyvEuRit6d4cFep43Ws="
matrix:
- BUILD_SOC=0
- BUILD_SOC=1
before_install:
- mkdir -p $HOME/.mlabs
- if [ $TRAVIS_PULL_REQUEST != false ]; then BUILD_SOC=0; fi
- if [ $BUILD_SOC -ne 0 ]; then ./.travis/get-xilinx.sh; fi
- . ./.travis/get-toolchain.sh
- . ./.travis/get-anaconda.sh
- echo "BUILD_SOC=$BUILD_SOC" >> $HOME/.mlabs/build_settings.sh
- source $HOME/miniconda/bin/activate py35
- conda install -q pip coverage anaconda-client migen cython
- pip install coveralls
- BUILD_SOC=none
- BUILD_SOC=pipistrello-nist_qc1
- BUILD_SOC=kc705-nist_qc1
- BUILD_SOC=kc705-nist_qc2
install:
- conda build --python 3.5 conda/artiq
- conda install -q artiq --use-local
- mkdir -p $HOME/.m-labs
- if [ $TRAVIS_PULL_REQUEST != false ]; then BUILD_SOC=none; fi
- if [ $BUILD_SOC != none ]; then ./.travis/get-xilinx.sh; fi
- if [ $BUILD_SOC != none ]; then ./.travis/get-toolchain.sh; fi
- if [ $BUILD_SOC != none ]; then ./.travis/get-misoc.sh; fi
- . ./.travis/get-anaconda.sh
- source $HOME/miniconda/bin/activate py35
- conda install -q pip coverage anaconda-client cython
- pip install coveralls
# workaround for https://github.com/conda/conda-build/issues/466
- mkdir -p /home/travis/miniconda/conda-bld/linux-64
- conda index /home/travis/miniconda/conda-bld/linux-64
script:
- coverage run --source=artiq setup.py test
- make -C doc/manual html
- conda build --python 3.5 conda/artiq
- conda install -q --use-local artiq
- |
if [ $BUILD_SOC == none ]; then
PACKAGES="$(conda build --output --python 3.5 conda/artiq) $PACKAGES"
coverage run --source=artiq setup.py test
make -C doc/manual html
else
PACKAGES="$(conda build --output --python 3.5 conda/artiq-$BUILD_SOC) $PACKAGES"
conda build --python 3.5 conda/artiq-$BUILD_SOC
fi
after_success:
- |
if [ "$TRAVIS_BRANCH" == "master" -a $BUILD_SOC -eq 1 ]; then
if [ "$TRAVIS_BRANCH" == "master" -a "$PACKAGES" != "" ]; then
anaconda -q login --hostname $(hostname) --username $binstar_login --password $binstar_password
anaconda -q upload --user $binstar_login --channel dev --force $HOME/miniconda/conda-bld/linux-64/artiq-*.tar.bz2
anaconda -q upload --user $binstar_login --channel dev $PACKAGES
anaconda -q logout
fi
- coveralls

View File

@ -10,4 +10,5 @@ conda update -q conda
conda info -a
conda install conda-build jinja2
conda create -q -n py35 python=$TRAVIS_PYTHON_VERSION
conda config --add channels https://conda.anaconda.org/m-labs/channel/main
conda config --add channels https://conda.anaconda.org/m-labs/channel/dev

4
.travis/get-misoc.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
git clone --recursive https://github.com/m-labs/misoc $HOME/misoc
echo "export MSCDIR=$HOME/misoc" >> $HOME/.m-labs/build_settings.sh

View File

@ -1,7 +1,6 @@
#!/bin/sh
packages="http://us.archive.ubuntu.com/ubuntu/pool/universe/i/iverilog/iverilog_0.9.7-1_amd64.deb"
archives="http://fehu.whitequark.org/files/llvm-or1k.tbz2"
mkdir -p packages
@ -12,18 +11,5 @@ do
dpkg -x $pkg_name packages
done
for a in $archives
do
wget $a
(cd packages && tar xf ../$(basename $a))
done
export PATH=$PWD/packages/usr/local/llvm-or1k/bin:$PWD/packages/usr/local/bin:$PWD/packages/usr/bin:$PATH
export LD_LIBRARY_PATH=$PWD/packages/usr/lib/x86_64-linux-gnu:$PWD/packages/usr/local/x86_64-unknown-linux-gnu/or1k-elf/lib:$LD_LIBRARY_PATH
echo "export LD_LIBRARY_PATH=$PWD/packages/usr/lib/x86_64-linux-gnu:$PWD/packages/usr/local/x86_64-unknown-linux-gnu/or1k-elf/lib:\$LD_LIBRARY_PATH" >> $HOME/.mlabs/build_settings.sh
echo "export PATH=$PWD/packages/usr/local/llvm-or1k/bin:$PWD/packages/usr/local/bin:$PWD/packages/usr/bin:\$PATH" >> $HOME/.mlabs/build_settings.sh
or1k-linux-as --version
llc --version
clang --version
echo "export LD_LIBRARY_PATH=$PWD/packages/usr/lib/x86_64-linux-gnu" >> $HOME/.m-labs/build_settings.sh
echo "export PATH=$PWD/packages/usr/bin:\$PATH" >> $HOME/.m-labs/build_settings.sh

View File

@ -30,7 +30,7 @@ git clone https://github.com/fallen/impersonate_macaddress
make -C impersonate_macaddress
# Tell mibuild where Xilinx toolchains are installed
# and feed it the mac address corresponding to the license
cat >> $HOME/.mlabs/build_settings.sh << EOF
cat >> $HOME/.m-labs/build_settings.sh << EOF
MISOC_EXTRA_VIVADO_CMDLINE="-Ob vivado_path $HOME/Xilinx/Vivado"
MISOC_EXTRA_ISE_CMDLINE="-Ob ise_path $HOME/opt/Xilinx/"
export MACADDR=$macaddress

View File

@ -139,6 +139,12 @@ class Pdq2:
self.num_channels = self.num_dacs * self.num_boards
self.channels = [Channel() for i in range(self.num_channels)]
def get_num_boards(self):
return self.num_boards
def get_num_channels(self):
return self.num_channels
def close(self):
self.dev.close()
del self.dev

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import logging
import time
import asyncio
import sys
@ -51,6 +52,10 @@ def get_argparser():
"(defaults to head, ignored without -R)")
parser_add.add_argument("-c", "--class-name", default=None,
help="name of the class to run")
parser_add.add_argument("-v", "--verbose", default=0, action="count",
help="increase logging level of the experiment")
parser_add.add_argument("-q", "--quiet", default=0, action="count",
help="decrease logging level of the experiment")
parser_add.add_argument("file",
help="file containing the experiment to run")
parser_add.add_argument("arguments", nargs="*",
@ -110,6 +115,7 @@ def _action_submit(remote, args):
sys.exit(1)
expid = {
"log_level": logging.WARNING + args.quiet*10 - args.verbose*10,
"file": args.file,
"class_name": args.class_name,
"arguments": arguments,

View File

@ -5,39 +5,22 @@ import atexit
import argparse
import os
import logging
import subprocess
import shlex
import socket
import platform
from artiq.protocols.sync_struct import Subscriber
from artiq.protocols.pc_rpc import AsyncioClient, Server
from artiq.tools import verbosity_args, init_logger
from artiq.protocols.logging import (LogForwarder,
parse_log_message, log_with_name,
SourceFilter)
from artiq.tools import TaskObject, Condition
logger = logging.getLogger(__name__)
def get_argparser():
parser = argparse.ArgumentParser(description="ARTIQ controller manager")
verbosity_args(parser)
parser.add_argument(
"-s", "--server", default="::1",
help="hostname or IP of the master to connect to")
parser.add_argument(
"--port", default=3250, type=int,
help="TCP port to use to connect to the master")
parser.add_argument(
"--retry-master", default=5.0, type=float,
help="retry timer for reconnecting to master")
parser.add_argument(
"--bind", default="::1",
help="hostname or IP address to bind to")
parser.add_argument(
"--bind-port", default=3249, type=int,
help="TCP port to listen to for control (default: %(default)d)")
return parser
class Controller:
def __init__(self, name, ddb_entry):
self.name = name
@ -96,6 +79,23 @@ class Controller:
else:
break
async def forward_logs(self, stream):
source = "controller({})".format(self.name)
while True:
try:
entry = (await stream.readline())
if not entry:
break
entry = entry[:-1]
level, name, message = parse_log_message(entry.decode())
log_with_name(name, level, message, extra={"source": source})
except:
logger.debug("exception in log forwarding", exc_info=True)
break
logger.debug("stopped log forwarding of stream %s of %s",
stream, self.name)
async def launcher(self):
try:
while True:
@ -103,7 +103,12 @@ class Controller:
self.name, self.command)
try:
self.process = await asyncio.create_subprocess_exec(
*shlex.split(self.command))
*shlex.split(self.command),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
asyncio.ensure_future(self.forward_logs(
self.process.stdout))
asyncio.ensure_future(self.forward_logs(
self.process.stderr))
await self._wait_and_ping()
except FileNotFoundError:
logger.warning("Controller %s failed to start", self.name)
@ -129,14 +134,20 @@ class Controller:
except:
logger.warning("Controller %s did not respond to terminate "
"command, killing", self.name)
try:
self.process.kill()
except ProcessLookupError:
pass
try:
await asyncio.wait_for(self.process.wait(),
self.term_timeout)
except:
logger.warning("Controller %s failed to exit, killing",
self.name)
try:
self.process.kill()
except ProcessLookupError:
pass
await self.process.wait()
logger.debug("Controller %s terminated", self.name)
@ -252,9 +263,48 @@ class ControllerManager(TaskObject):
self.controller_db.current_controllers.active[k].retry_now.notify()
def get_argparser():
parser = argparse.ArgumentParser(description="ARTIQ controller manager")
group = parser.add_argument_group("verbosity")
group.add_argument("-v", "--verbose", default=0, action="count",
help="increase logging level of the manager process")
group.add_argument("-q", "--quiet", default=0, action="count",
help="decrease logging level of the manager process")
parser.add_argument(
"-s", "--server", default="::1",
help="hostname or IP of the master to connect to")
parser.add_argument(
"--port-notify", default=3250, type=int,
help="TCP port to connect to for notifications")
parser.add_argument(
"--port-logging", default=1066, type=int,
help="TCP port to connect to for logging")
parser.add_argument(
"--retry-master", default=5.0, type=float,
help="retry timer for reconnecting to master")
parser.add_argument(
"--bind", default="::1",
help="hostname or IP address to bind to")
parser.add_argument(
"--bind-port", default=3249, type=int,
help="TCP port to listen to for control (default: %(default)d)")
return parser
def main():
args = get_argparser().parse_args()
init_logger(args)
root_logger = logging.getLogger()
root_logger.setLevel(logging.NOTSET)
source_adder = SourceFilter(logging.WARNING + args.quiet*10 - args.verbose*10,
"ctlmgr({})".format(platform.node()))
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(
"%(levelname)s:%(source)s:%(name)s:%(message)s"))
console_handler.addFilter(source_adder)
root_logger.addHandler(console_handler)
if os.name == "nt":
loop = asyncio.ProactorEventLoop()
@ -263,7 +313,15 @@ def main():
loop = asyncio.get_event_loop()
atexit.register(lambda: loop.close())
ctlmgr = ControllerManager(args.server, args.port, args.retry_master)
logfwd = LogForwarder(args.server, args.port_logging,
args.retry_master)
logfwd.addFilter(source_adder)
root_logger.addHandler(logfwd)
logfwd.start()
atexit.register(lambda: loop.run_until_complete(logfwd.stop()))
ctlmgr = ControllerManager(args.server, args.port_notify,
args.retry_master)
ctlmgr.start()
atexit.register(lambda: loop.run_until_complete(ctlmgr.stop()))

View File

@ -1,5 +1,16 @@
#!/bin/bash
#!/usr/bin/env python
# conda-build requires all scripts to have a python shebang.
# see https://github.com/conda/conda-build/blob/6921f067a/conda_build/noarch_python.py#L36-L38
def run(script):
import sys, tempfile, subprocess
file = tempfile.NamedTemporaryFile(mode='w+t', suffix='sh')
file.write(script)
file.flush()
subprocess.run(["/bin/bash", file.name] + sys.argv[1:])
file.close()
run("""
# exit on error
set -e
# print commands
@ -72,7 +83,7 @@ do
echo ""
echo "To flash everything, do not use any of the -b|-B|-r option."
echo ""
echo "usage: $0 [-b] [-B] [-r] [-h] [-m nist_qc1|nist_qc2] [-t kc705|pipistrello] [-d path] [-f path]"
echo "usage: artiq_flash.sh [-b] [-B] [-r] [-h] [-m nist_qc1|nist_qc2] [-t kc705|pipistrello] [-d path] [-f path]"
echo "-b Flash bitstream"
echo "-B Flash BIOS"
echo "-r Flash ARTIQ runtime"
@ -193,3 +204,4 @@ then
fi
echo "Done."
xc3sprog -v -c $CABLE -R > /dev/null 2>&1
""")

View File

@ -116,6 +116,7 @@ def main():
atexit.register(lambda: loop.run_until_complete(d_schedule.sub_close()))
d_log = LogDock()
smgr.register(d_log)
loop.run_until_complete(d_log.sub_connect(
args.server, args.port_notify))
atexit.register(lambda: loop.run_until_complete(d_log.sub_close()))

View File

@ -5,9 +5,10 @@ import argparse
import atexit
import os
from artiq.protocols.pc_rpc import Server
from artiq.protocols.pc_rpc import Server as RPCServer
from artiq.protocols.sync_struct import Publisher
from artiq.master.log import log_args, init_log
from artiq.protocols.logging import Server as LoggingServer
from artiq.master.log import log_args, init_log, log_worker
from artiq.master.databases import DeviceDB, DatasetDB
from artiq.master.scheduler import Scheduler
from artiq.master.worker_db import get_last_rid
@ -27,6 +28,9 @@ def get_argparser():
group.add_argument(
"--port-control", default=3251, type=int,
help="TCP port to listen to for control (default: %(default)d)")
group.add_argument(
"--port-logging", default=1066, type=int,
help="TCP port to listen to for remote logging (default: %(default)d)")
group = parser.add_argument_group("databases")
group.add_argument("--device-db", default="device_db.pyon",
@ -49,7 +53,7 @@ def get_argparser():
def main():
args = get_argparser().parse_args()
log_buffer, log_forwarder = init_log(args)
log_buffer = init_log(args)
if os.name == "nt":
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
@ -67,7 +71,7 @@ def main():
else:
repo_backend = FilesystemBackend(args.repository)
repository = Repository(repo_backend, device_db.get_device_db,
log_forwarder.log_worker)
log_worker)
atexit.register(repository.close)
repository.scan_async()
@ -76,14 +80,14 @@ def main():
"get_device": device_db.get,
"get_dataset": dataset_db.get,
"update_dataset": dataset_db.update,
"log": log_forwarder.log_worker
"log": log_worker
}
scheduler = Scheduler(get_last_rid() + 1, worker_handlers, repo_backend)
worker_handlers["scheduler_submit"] = scheduler.submit
scheduler.start()
atexit.register(lambda: loop.run_until_complete(scheduler.stop()))
server_control = Server({
server_control = RPCServer({
"master_device_db": device_db,
"master_dataset_db": dataset_db,
"master_schedule": scheduler,
@ -104,6 +108,11 @@ def main():
args.bind, args.port_notify))
atexit.register(lambda: loop.run_until_complete(server_notify.stop()))
server_logging = LoggingServer()
loop.run_until_complete(server_logging.start(
args.bind, args.port_logging))
atexit.register(lambda: loop.run_until_complete(server_logging.stop()))
loop.run_forever()
if __name__ == "__main__":

View File

@ -6,7 +6,7 @@ import sys
import numpy as np # Needed to use numpy in RPC call arguments on cmd line
import pprint
from artiq.protocols.pc_rpc import Client
from artiq.protocols.pc_rpc import AutoTarget, Client
def get_argparser():
@ -85,19 +85,9 @@ def main():
args = get_argparser().parse_args()
remote = Client(args.server, args.port, None)
targets, description = remote.get_rpc_id()
if args.action != "list-targets":
# If no target specified and remote has only one, then use this one.
# Exit otherwise.
if len(targets) > 1 and args.target is None:
print("Remote server has several targets, please supply one with "
"-t")
sys.exit(1)
elif args.target is None:
args.target = targets[0]
remote.select_rpc_target(args.target)
remote.select_rpc_target(AutoTarget)
if args.action == "list-targets":
list_targets(targets, description)

View File

@ -55,7 +55,6 @@ def main():
args = get_argparser().parse_args()
init_logger(args)
dev = Client(args.server, args.port, "pdq2")
dev.init()
if args.reset:
dev.write(b"\x00\x00") # flush any escape
@ -66,8 +65,6 @@ def main():
dev.cmd("DCM", args.dcm)
freq = 100e6 if args.dcm else 50e6
dev.set_freq(freq)
num_channels = dev.get_num_channels()
num_frames = dev.get_num_frames()
times = eval(args.times, globals(), {})
voltages = eval(args.voltages, globals(), dict(t=times))

View File

@ -12,7 +12,7 @@ def get_argparser():
parser = argparse.ArgumentParser()
parser.add_argument("-P", "--product", required=True,
help="type of the Thorlabs T-Cube device to control",
choices=["TDC001", "TPZ001"])
choices=["tdc001", "tpz001"])
parser.add_argument("-d", "--device", default=None,
help="serial device. See documentation for how to "
"specify a USB Serial Number.")
@ -33,19 +33,20 @@ def main():
"argument. Use --help for more information.")
sys.exit(1)
product = args.product.lower()
if args.simulation:
if args.product == "TDC001":
if product == "tdc001":
dev = TdcSim()
elif args.product == "TPZ001":
elif product == "tpz001":
dev = TpzSim()
else:
if args.product == "TDC001":
if product == "tdc001":
dev = Tdc(args.device)
elif args.product == "TPZ001":
elif product == "tpz001":
dev = Tpz(args.device)
try:
simple_server_loop({args.product.lower(): dev}, args.bind, args.port)
simple_server_loop({product: dev}, args.bind, args.port)
finally:
dev.close()

View File

@ -12,6 +12,11 @@ from artiq.tools import short_format
from artiq.gui.tools import DictSyncModel
from artiq.gui.displays import *
try:
QSortFilterProxyModel = QtCore.QSortFilterProxyModel
except AttributeError:
QSortFilterProxyModel = QtGui.QSortFilterProxyModel
logger = logging.getLogger(__name__)
@ -74,15 +79,7 @@ class DatasetsDock(dockarea.Dock):
self.displays = dict()
def _search_datasets(self):
model = self.table_model
search = self.search.displayText()
for row in range(model.rowCount(model.index(0, 0))):
index = model.index(row, 0)
dataset = model.data(index, QtCore.Qt.DisplayRole)
if search in dataset:
self.table.showRow(row)
else:
self.table.hideRow(row)
self.table_model_filter.setFilterFixedString(self.search.displayText())
def get_dataset(self, key):
return self.table_model.backing_store[key][1]
@ -97,12 +94,16 @@ class DatasetsDock(dockarea.Dock):
def init_datasets_model(self, init):
self.table_model = DatasetsModel(self.table, init)
self.table.setModel(self.table_model)
self.table_model_filter = QSortFilterProxyModel()
self.table_model_filter.setSourceModel(self.table_model)
self.table.setModel(self.table_model_filter)
return self.table_model
def update_display_data(self, dsp):
dsp.update_data({k: self.table_model.backing_store[k][1]
for k in dsp.data_sources()})
filtered_data = {k: self.table_model.backing_store[k][1]
for k in dsp.data_sources()
if k in self.table_model.backing_store}
dsp.update_data(filtered_data)
def on_mod(self, mod):
if mod["action"] == "init":
@ -110,10 +111,10 @@ class DatasetsDock(dockarea.Dock):
display.update_data(self.table_model.backing_store)
return
if mod["action"] == "setitem":
source = mod["key"]
elif mod["path"]:
if mod["path"]:
source = mod["path"][0]
elif mod["action"] == "setitem":
source = mod["key"]
else:
return

View File

@ -137,7 +137,7 @@ class XYDisplay(dockarea.Dock):
error = data.get(result_error, None)
fit = data.get(result_fit, None)
if not y or len(y) != len(x):
if not len(y) or len(y) != len(x):
return
if error is not None and hasattr(error, "__len__"):
if not len(error):
@ -201,7 +201,7 @@ class HistogramDisplay(dockarea.Dock):
if x is None:
x = list(range(len(y)+1))
if y and len(x) == len(y) + 1:
if len(y) and len(x) == len(y) + 1:
self.plot.clear()
self.plot.plot(x, y, stepMode=True, fillLevel=0,
brush=(0, 0, 255, 150))

View File

@ -1,4 +1,5 @@
import asyncio
import logging
from quamash import QtGui, QtCore
from pyqtgraph import dockarea
@ -6,8 +7,9 @@ from pyqtgraph import LayoutWidget
from artiq.protocols.sync_struct import Subscriber
from artiq.protocols import pyon
from artiq.gui.tools import si_prefix, DictSyncModel
from artiq.gui.tools import DictSyncModel
from artiq.gui.scan import ScanController
from artiq.gui.shortcuts import ShortcutManager
class _ExplistModel(DictSyncModel):
@ -85,9 +87,8 @@ class _NumberEntry(QtGui.QDoubleSpinBox):
self.setMaximum(procdesc["max"]/self.scale)
else:
self.setMaximum(float("inf"))
suffix = si_prefix(self.scale) + procdesc["unit"]
if suffix:
self.setSuffix(" " + suffix)
if procdesc["unit"]:
self.setSuffix(" " + procdesc["unit"])
if "default" in procdesc:
self.set_argument_value(procdesc["default"])
@ -122,14 +123,14 @@ _procty_to_entry = {
class _ArgumentEditor(QtGui.QTreeWidget):
def __init__(self, dialog_parent):
def __init__(self, main_window):
QtGui.QTreeWidget.__init__(self)
self.setColumnCount(2)
self.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
self.header().setVisible(False)
self.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.dialog_parent = dialog_parent
self.main_window = main_window
self._groups = dict()
self.set_arguments([])
@ -176,7 +177,7 @@ class _ArgumentEditor(QtGui.QTreeWidget):
r[arg] = entry.get_argument_value()
except Exception as e:
if show_error_message:
msgbox = QtGui.QMessageBox(self.dialog_parent)
msgbox = QtGui.QMessageBox(self.main_window)
msgbox.setWindowTitle("Error")
msgbox.setText("Failed to obtain value for argument '{}':\n{}"
.format(arg, str(e)))
@ -215,10 +216,10 @@ class _ArgumentEditor(QtGui.QTreeWidget):
class ExplorerDock(dockarea.Dock):
def __init__(self, dialog_parent, status_bar, schedule_ctl):
def __init__(self, main_window, status_bar, schedule_ctl):
dockarea.Dock.__init__(self, "Explorer", size=(1500, 500))
self.dialog_parent = dialog_parent
self.main_window = main_window
self.status_bar = status_bar
self.schedule_ctl = schedule_ctl
@ -235,44 +236,59 @@ class ExplorerDock(dockarea.Dock):
self.datetime = QtGui.QDateTimeEdit()
self.datetime.setDisplayFormat("MMM d yyyy hh:mm:ss")
self.datetime.setCalendarPopup(True)
self.datetime.setDate(QtCore.QDate.currentDate())
self.datetime.dateTimeChanged.connect(self.enable_duedate)
self.datetime_en = QtGui.QCheckBox("Due date:")
grid.addWidget(self.datetime_en, 1, 0)
grid.addWidget(self.datetime, 1, 1)
self.priority = QtGui.QSpinBox()
self.priority.setRange(-99, 99)
grid.addWidget(QtGui.QLabel("Priority:"), 1, 2)
grid.addWidget(self.priority, 1, 3)
grid.addWidget(self.datetime_en, 1, 0, colspan=2)
grid.addWidget(self.datetime, 1, 2, colspan=2)
self.pipeline = QtGui.QLineEdit()
self.pipeline.setText("main")
grid.addWidget(QtGui.QLabel("Pipeline:"), 2, 0)
grid.addWidget(self.pipeline, 2, 1)
grid.addWidget(QtGui.QLabel("Pipeline:"), 2, 0, colspan=2)
grid.addWidget(self.pipeline, 2, 2, colspan=2)
self.priority = QtGui.QSpinBox()
self.priority.setRange(-99, 99)
grid.addWidget(QtGui.QLabel("Priority:"), 3, 0)
grid.addWidget(self.priority, 3, 1)
self.flush = QtGui.QCheckBox("Flush")
grid.addWidget(self.flush, 2, 2, colspan=2)
self.flush.setToolTip("Flush the pipeline before starting the experiment")
grid.addWidget(self.flush, 3, 2)
self.log_level = QtGui.QComboBox()
self.log_level.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
self.log_level.setCurrentIndex(1)
self.log_level.setToolTip("Minimum level for log entry production")
grid.addWidget(self.log_level, 3, 3)
submit = QtGui.QPushButton("Submit")
grid.addWidget(submit, 3, 0, colspan=4)
submit.setShortcut("CTRL+RETURN")
submit.setToolTip("Schedule the selected experiment (CTRL+ENTER)")
grid.addWidget(submit, 4, 0, colspan=4)
submit.clicked.connect(self.submit_clicked)
self.argeditor = _ArgumentEditor(self.dialog_parent)
self.argeditor = _ArgumentEditor(self.main_window)
self.splitter.addWidget(self.argeditor)
self.splitter.setSizes([grid.minimumSizeHint().width(), 1000])
self.state = dict()
self.argeditor_states = dict()
self.shortcuts = ShortcutManager(self.main_window, self)
self.el.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
edit_shortcuts_action = QtGui.QAction("Edit shortcuts", self.el)
edit_shortcuts_action.triggered.connect(self.edit_shortcuts)
self.el.addAction(edit_shortcuts_action)
def update_selection(self, selected, deselected):
if deselected:
self.state[deselected] = self.argeditor.save_state()
self.argeditor_states[deselected] = self.argeditor.save_state()
if selected:
expinfo = self.explist_model.backing_store[selected]
self.argeditor.set_arguments(expinfo["arguments"])
if selected in self.state:
self.argeditor.restore_state(self.state[selected])
if selected in self.argeditor_states:
self.argeditor.restore_state(self.argeditor_states[selected])
self.splitter.insertWidget(1, self.argeditor)
self.selected_key = selected
@ -293,11 +309,20 @@ class ExplorerDock(dockarea.Dock):
if idx:
row = idx[0].row()
key = self.explist_model.row_to_key[row]
self.state[key] = self.argeditor.save_state()
return self.state
self.argeditor_states[key] = self.argeditor.save_state()
return {
"argeditor": self.argeditor_states,
"shortcuts": self.shortcuts.save_state()
}
def restore_state(self, state):
self.state = state
try:
argeditor_states = state["argeditor"]
shortcuts_state = state["shortcuts"]
except KeyError:
return
self.argeditor_states = argeditor_states
self.shortcuts.restore_state(shortcuts_state)
def enable_duedate(self):
self.datetime_en.setChecked(True)
@ -315,9 +340,10 @@ class ExplorerDock(dockarea.Dock):
self.el.setModel(self.explist_model)
return self.explist_model
async def submit(self, pipeline_name, file, class_name, arguments,
async def submit_task(self, pipeline_name, file, class_name, arguments,
priority, due_date, flush):
expid = {
"log_level": getattr(logging, self.log_level.currentText()),
"repo_rev": None,
"file": file,
"class_name": class_name,
@ -327,20 +353,41 @@ class ExplorerDock(dockarea.Dock):
priority, due_date, flush)
self.status_bar.showMessage("Submitted RID {}".format(rid))
def submit(self, pipeline, key, priority, due_date, flush):
# TODO: refactor explorer and cleanup.
# Argument editors should immediately modify the global state.
expinfo = self.explist_model.backing_store[key]
if key == self.selected_key:
arguments = self.argeditor.get_argument_values(True)
if arguments is None:
# There has been an error. Displaying the error message box
# was done by argeditor.
return
else:
try:
arguments = self.argeditor_states[key]["argument_values"]
except KeyError:
arguments = dict()
asyncio.ensure_future(self.submit_task(self.pipeline.text(),
expinfo["file"],
expinfo["class_name"],
arguments,
priority,
due_date,
flush))
def submit_clicked(self):
if self.selected_key is not None:
expinfo = self.explist_model.backing_store[self.selected_key]
if self.datetime_en.isChecked():
due_date = self.datetime.dateTime().toMSecsSinceEpoch()/1000
else:
due_date = None
arguments = self.argeditor.get_argument_values(True)
if arguments is None:
return
asyncio.ensure_future(self.submit(self.pipeline.text(),
expinfo["file"],
expinfo["class_name"],
arguments,
self.submit(self.pipeline.text(),
self.selected_key,
self.priority.value(),
due_date,
self.flush.isChecked()))
self.flush.isChecked())
def edit_shortcuts(self):
experiments = sorted(self.explist_model.backing_store.keys())
self.shortcuts.edit(experiments)

View File

@ -3,11 +3,16 @@ import logging
import time
from quamash import QtGui, QtCore
from pyqtgraph import dockarea
from pyqtgraph import dockarea, LayoutWidget
from artiq.protocols.sync_struct import Subscriber
from artiq.gui.tools import ListSyncModel
try:
QSortFilterProxyModel = QtCore.QSortFilterProxyModel
except AttributeError:
QSortFilterProxyModel = QtGui.QSortFilterProxyModel
def _level_to_name(level):
if level >= logging.CRITICAL:
@ -20,6 +25,7 @@ def _level_to_name(level):
return "INFO"
return "DEBUG"
class _LogModel(ListSyncModel):
def __init__(self, parent, init):
ListSyncModel.__init__(self,
@ -66,10 +72,39 @@ class _LogModel(ListSyncModel):
return v[3]
class _LevelFilterProxyModel(QSortFilterProxyModel):
def __init__(self, min_level):
QSortFilterProxyModel.__init__(self)
self.min_level = min_level
def filterAcceptsRow(self, sourceRow, sourceParent):
model = self.sourceModel()
index = model.index(sourceRow, 0, sourceParent)
data = model.data(index, QtCore.Qt.DisplayRole)
return getattr(logging, data) >= self.min_level
def set_min_level(self, min_level):
self.min_level = min_level
self.invalidateFilter()
class LogDock(dockarea.Dock):
def __init__(self):
dockarea.Dock.__init__(self, "Log", size=(1000, 300))
grid = LayoutWidget()
self.addWidget(grid)
grid.addWidget(QtGui.QLabel("Minimum level: "), 0, 0)
grid.layout.setColumnStretch(0, 0)
grid.layout.setColumnStretch(1, 0)
grid.layout.setColumnStretch(2, 1)
self.filterbox = QtGui.QComboBox()
self.filterbox.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
self.filterbox.setToolTip("Display entries at or above this level")
grid.addWidget(self.filterbox, 0, 1)
self.filterbox.currentIndexChanged.connect(self.filter_changed)
self.log = QtGui.QTableView()
self.log.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.log.horizontalHeader().setResizeMode(
@ -78,7 +113,7 @@ class LogDock(dockarea.Dock):
QtGui.QAbstractItemView.ScrollPerPixel)
self.log.setShowGrid(False)
self.log.setTextElideMode(QtCore.Qt.ElideNone)
self.addWidget(self.log)
grid.addWidget(self.log, 1, 0, colspan=3)
self.scroll_at_bottom = False
async def sub_connect(self, host, port):
@ -88,6 +123,10 @@ class LogDock(dockarea.Dock):
async def sub_close(self):
await self.subscriber.close()
def filter_changed(self):
self.table_model_filter.set_min_level(
getattr(logging, self.filterbox.currentText()))
def rows_inserted_before(self):
scrollbar = self.log.verticalScrollBar()
self.scroll_at_bottom = scrollbar.value() == scrollbar.maximum()
@ -98,7 +137,21 @@ class LogDock(dockarea.Dock):
def init_log_model(self, init):
table_model = _LogModel(self.log, init)
self.log.setModel(table_model)
table_model.rowsAboutToBeInserted.connect(self.rows_inserted_before)
table_model.rowsInserted.connect(self.rows_inserted_after)
self.table_model_filter = _LevelFilterProxyModel(
getattr(logging, self.filterbox.currentText()))
self.table_model_filter.setSourceModel(table_model)
self.log.setModel(self.table_model_filter)
self.table_model_filter.rowsAboutToBeInserted.connect(self.rows_inserted_before)
self.table_model_filter.rowsInserted.connect(self.rows_inserted_after)
return table_model
def save_state(self):
return {"min_level_idx": self.filterbox.currentIndex()}
def restore_state(self, state):
try:
idx = state["min_level_idx"]
except KeyError:
pass
else:
self.filterbox.setCurrentIndex(idx)

View File

@ -1,11 +1,9 @@
from quamash import QtGui
from pyqtgraph import LayoutWidget
from artiq.gui.tools import si_prefix
class _Range(LayoutWidget):
def __init__(self, global_min, global_max, global_step, suffix, scale, ndecimals):
def __init__(self, global_min, global_max, global_step, unit, scale, ndecimals):
LayoutWidget.__init__(self)
self.scale = scale
@ -21,8 +19,8 @@ class _Range(LayoutWidget):
spinbox.setMaximum(float("inf"))
if global_step is not None:
spinbox.setSingleStep(global_step/self.scale)
if suffix:
spinbox.setSuffix(" " + suffix)
if unit:
spinbox.setSuffix(" " + unit)
self.addWidget(QtGui.QLabel("Min:"), 0, 0)
self.min = QtGui.QDoubleSpinBox()
@ -68,7 +66,7 @@ class ScanController(LayoutWidget):
gmin, gmax = procdesc["global_min"], procdesc["global_max"]
gstep = procdesc["global_step"]
suffix = si_prefix(self.scale) + procdesc["unit"]
unit = procdesc["unit"]
ndecimals = procdesc["ndecimals"]
self.v_noscan = QtGui.QDoubleSpinBox()
@ -82,17 +80,17 @@ class ScanController(LayoutWidget):
else:
self.v_noscan.setMaximum(float("inf"))
self.v_noscan.setSingleStep(gstep/self.scale)
if suffix:
self.v_noscan.setSuffix(" " + suffix)
if unit:
self.v_noscan.setSuffix(" " + unit)
self.v_noscan_gr = LayoutWidget()
self.v_noscan_gr.addWidget(QtGui.QLabel("Value:"), 0, 0)
self.v_noscan_gr.addWidget(self.v_noscan, 0, 1)
self.stack.addWidget(self.v_noscan_gr)
self.v_linear = _Range(gmin, gmax, gstep, suffix, self.scale, ndecimals)
self.v_linear = _Range(gmin, gmax, gstep, unit, self.scale, ndecimals)
self.stack.addWidget(self.v_linear)
self.v_random = _Range(gmin, gmax, gstep, suffix, self.scale, ndecimals)
self.v_random = _Range(gmin, gmax, gstep, unit, self.scale, ndecimals)
self.stack.addWidget(self.v_random)
self.v_explicit = QtGui.QLineEdit()

View File

@ -75,9 +75,11 @@ class ScheduleDock(dockarea.Dock):
self.table.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
request_termination_action = QtGui.QAction("Request termination", self.table)
request_termination_action.triggered.connect(partial(self.delete_clicked, True))
request_termination_action.setShortcut("DELETE")
self.table.addAction(request_termination_action)
delete_action = QtGui.QAction("Delete", self.table)
delete_action.triggered.connect(partial(self.delete_clicked, False))
delete_action.setShortcut("SHIFT+DELETE")
self.table.addAction(delete_action)
@ -104,5 +106,9 @@ class ScheduleDock(dockarea.Dock):
if idx:
row = idx[0].row()
rid = self.table_model.row_to_key[row]
self.status_bar.showMessage("Deleted RID {}".format(rid))
if graceful:
msg = "Requested termination of RID {}".format(rid)
else:
msg = "Deleted RID {}".format(rid)
self.status_bar.showMessage(msg)
asyncio.ensure_future(self.delete(rid, graceful))

98
artiq/gui/shortcuts.py Normal file
View File

@ -0,0 +1,98 @@
from functools import partial
from quamash import QtGui
try:
from quamash import QtWidgets
QShortcut = QtWidgets.QShortcut
except:
QShortcut = QtGui.QShortcut
class _ShortcutEditor(QtGui.QDialog):
def __init__(self, parent, experiments, shortcuts):
QtGui.QDialog.__init__(self, parent=parent)
self.setWindowTitle("Shortcuts")
self.shortcuts = shortcuts
self.edit_widgets = dict()
grid = QtGui.QGridLayout()
self.setLayout(grid)
for n, title in enumerate(["Key", "Experiment", "Priority", "Pipeline"]):
label = QtGui.QLabel("<b>" + title + "</b")
grid.addWidget(label, 0, n)
label.setMaximumHeight(label.sizeHint().height())
grid.setColumnStretch(1, 1)
grid.setColumnStretch(3, 1)
for i in range(12):
row = i + 1
existing_shortcut = self.shortcuts.get(i, dict())
grid.addWidget(QtGui.QLabel("F" + str(i+1)), row, 0)
experiment = QtGui.QComboBox()
grid.addWidget(experiment, row, 1)
experiment.addItem("<None>")
experiment.addItems(experiments)
experiment.setEditable(True)
experiment.setEditText(
existing_shortcut.get("experiment", "<None>"))
priority = QtGui.QSpinBox()
grid.addWidget(priority, row, 2)
priority.setRange(-99, 99)
priority.setValue(existing_shortcut.get("priority", 0))
pipeline = QtGui.QLineEdit()
grid.addWidget(pipeline, row, 3)
pipeline.setText(existing_shortcut.get("pipeline", "main"))
self.edit_widgets[i] = {
"experiment": experiment,
"priority": priority,
"pipeline": pipeline
}
buttons = QtGui.QDialogButtonBox(
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
grid.addWidget(buttons, 14, 0, 1, 4)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
self.accepted.connect(self.on_accept)
def on_accept(self):
for n, widgets in self.edit_widgets.items():
self.shortcuts[n] = {
"experiment": widgets["experiment"].currentText(),
"priority": widgets["priority"].value(),
"pipeline": widgets["pipeline"].text()
}
class ShortcutManager:
def __init__(self, main_window, explorer):
for i in range(12):
shortcut = QShortcut("F" + str(i+1), main_window)
shortcut.activated.connect(partial(self._activated, i))
self.main_window = main_window
self.explorer = explorer
self.shortcuts = dict()
def edit(self, experiments):
dlg = _ShortcutEditor(self.main_window, experiments, self.shortcuts)
dlg.open()
def _activated(self, nr):
info = self.shortcuts.get(nr, dict())
experiment = info.get("experiment", "")
if experiment and experiment != "<None>":
self.explorer.submit(info["pipeline"], experiment,
info["priority"], None, False)
def save_state(self):
return self.shortcuts
def restore_state(self, state):
self.shortcuts = state

View File

@ -1,22 +1,4 @@
from quamash import QtCore
import numpy as np
def si_prefix(scale):
try:
return {
1e-12: "p",
1e-9: "n",
1e-6: "u",
1e-3: "m",
1.0: "",
1e3: "k",
1e6: "M",
1e9: "G",
1e12: "T"
}[scale]
except KeyError:
return "[x{}]".format(scale)
class _SyncSubstruct:
@ -95,7 +77,7 @@ class DictSyncModel(QtCore.QAbstractTableModel):
new_row = self._find_row(k, v)
if old_row == new_row:
self.dataChanged.emit(self.index(old_row, 0),
self.index(old_row, len(self.headers)))
self.index(old_row, len(self.headers)-1))
else:
self.beginMoveRows(QtCore.QModelIndex(), old_row, old_row,
QtCore.QModelIndex(), new_row)
@ -157,7 +139,7 @@ class ListSyncModel(QtCore.QAbstractTableModel):
def __setitem__(self, k, v):
self.dataChanged.emit(self.index(k, 0),
self.index(k, len(self.headers)))
self.index(k, len(self.headers)-1))
self.backing_store[k] = v
def __delitem__(self, k):

View File

@ -73,8 +73,7 @@ class NumberValue(_SimpleArgProcessor):
:param unit: A string representing the unit of the value, for user
interface (UI) purposes.
:param scale: The scale of value for UI purposes. The corresponding SI
prefix is shown in front of the unit, and the displayed value is
:param scale: The scale of value for UI purposes. The displayed value is
divided by the scale.
:param step: The step with which the value should be modified by up/down
buttons in a UI. The default is the scale divided by 10.
@ -209,9 +208,15 @@ class HasEnvironment:
broadcast=False, persist=False, save=True):
"""Sets the contents and handling modes of a dataset.
If the dataset is broadcasted, it must be PYON-serializable.
If the dataset is saved, it must be a scalar (``bool``, ``int``,
``float`` or NumPy scalar) or a NumPy array.
:param broadcast: the data is sent in real-time to the master, which
dispatches it. Returns a Notifier that can be used to mutate the dataset.
:param persist: the master should store the data on-disk. Implies broadcast.
dispatches it. Returns a Notifier that can be used to mutate the
dataset.
:param persist: the master should store the data on-disk. Implies
broadcast.
:param save: the data is saved into the local storage of the current
run (archived as a HDF5 file).
"""

View File

@ -140,8 +140,7 @@ class Scannable:
by 10.
:param unit: A string representing the unit of the scanned variable, for user
interface (UI) purposes.
:param scale: The scale of value for UI purposes. The corresponding SI
prefix is shown in front of the unit, and the displayed value is
:param scale: The scale of value for UI purposes. The displayed value is
divided by the scale.
:param ndecimals: The number of decimals a UI should use.
"""

View File

@ -32,7 +32,10 @@ class DatasetDB(TaskObject):
self.persist_file = persist_file
self.autosave_period = autosave_period
try:
file_data = pyon.load_file(self.persist_file)
except FileNotFoundError:
file_data = dict()
self.data = Notifier({k: (True, v) for k, v in file_data.items()})
def save(self):

View File

@ -1,6 +1,8 @@
import logging
import logging.handlers
from artiq.protocols.sync_struct import Notifier
from artiq.protocols.logging import parse_log_message, log_with_name, SourceFilter
class LogBuffer:
@ -18,88 +20,65 @@ class LogBufferHandler(logging.Handler):
def __init__(self, log_buffer, *args, **kwargs):
logging.Handler.__init__(self, *args, **kwargs)
self.log_buffer = log_buffer
self.setFormatter(logging.Formatter("%(name)s:%(message)s"))
def emit(self, record):
message = self.format(record)
self.log_buffer.log(record.levelno, record.source, record.created, message)
for part in message.split("\n"):
self.log_buffer.log(record.levelno, record.source, record.created,
part)
name_to_level = {
"CRITICAL": logging.CRITICAL,
"ERROR": logging.ERROR,
"WARN": logging.WARNING,
"WARNING": logging.WARNING,
"INFO": logging.INFO,
"DEBUG": logging.DEBUG,
}
def parse_log_message(msg):
for name, level in name_to_level.items():
if msg.startswith(name + ":"):
remainder = msg[len(name) + 1:]
try:
idx = remainder.index(":")
except:
continue
return level, remainder[:idx], remainder[idx+1:]
return logging.INFO, "print", msg
fwd_logger = logging.getLogger("fwd")
class LogForwarder:
def log_worker(self, rid, message):
def log_worker(rid, message):
level, name, message = parse_log_message(message)
fwd_logger.name = name
fwd_logger.log(level, message,
log_with_name(name, level, message,
extra={"source": "worker({})".format(rid)})
log_worker.worker_pass_rid = True
class SourceFilter:
def __init__(self, master_level):
self.master_level = master_level
def filter(self, record):
if not hasattr(record, "source"):
record.source = "master"
if record.source == "master":
return record.levelno >= self.master_level
else:
# log messages that are forwarded from a source have already
# been filtered, and may have a level below the master level.
return True
log_worker.worker_pass_rid = True
def log_args(parser):
group = parser.add_argument_group("verbosity")
group = parser.add_argument_group("logging")
group.add_argument("-v", "--verbose", default=0, action="count",
help="increase logging level for the master process")
help="increase logging level of the master process")
group.add_argument("-q", "--quiet", default=0, action="count",
help="decrease logging level for the master process")
help="decrease logging level of the master process")
group.add_argument("--log-file", default="",
help="store logs in rotated files; set the "
"base filename")
group.add_argument("--log-max-size", type=int, default=1024,
help="maximum size of each log file in KiB "
"(default: %(default)d)")
group.add_argument("--log-backup-count", type=int, default=6,
help="number of old log files to keep (.<n> is added "
"to the base filename (default: %(default)d)")
def init_log(args):
root_logger = logging.getLogger()
root_logger.setLevel(logging.NOTSET) # we use our custom filter only
flt = SourceFilter(logging.WARNING + args.quiet*10 - args.verbose*10)
flt = SourceFilter(logging.WARNING + args.quiet*10 - args.verbose*10,
"master")
handlers = []
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(levelname)s:%(source)s:%(name)s:%(message)s"))
console_handler.setFormatter(logging.Formatter(
"%(levelname)s:%(source)s:%(name)s:%(message)s"))
handlers.append(console_handler)
if args.log_file:
file_handler = logging.handlers.RotatingFileHandler(
args.log_file,
maxBytes=args.log_max_size*1024,
backupCount=args.log_backup_count)
file_handler.setFormatter(logging.Formatter(
"%(asctime)s %(levelname)s:%(source)s:%(name)s:%(message)s"))
handlers.append(file_handler)
log_buffer = LogBuffer(1000)
buffer_handler = LogBufferHandler(log_buffer)
buffer_handler.setFormatter(logging.Formatter("%(name)s:%(message)s"))
handlers.append(buffer_handler)
for handler in handlers:
handler.addFilter(flt)
root_logger.addHandler(handler)
log_forwarder = LogForwarder()
return log_buffer, log_forwarder
return log_buffer

View File

@ -229,8 +229,8 @@ class PrepareStage(TaskObject):
await run.prepare()
except:
logger.error("got worker exception in prepare stage, "
"deleting RID %d",
run.rid, exc_info=True)
"deleting RID %d", run.rid)
logger.debug("worker exception details", exc_info=True)
self.delete_cb(run.rid)
else:
run.status = RunStatus.prepare_done
@ -279,8 +279,8 @@ class RunStage(TaskObject):
completed = await run.run()
except:
logger.error("got worker exception in run stage, "
"deleting RID %d",
run.rid, exc_info=True)
"deleting RID %d", run.rid)
logger.debug("worker exception details", exc_info=True)
self.delete_cb(run.rid)
else:
if completed:
@ -317,8 +317,8 @@ class AnalyzeStage(TaskObject):
await run.write_results()
except:
logger.error("got worker exception in analyze stage, "
"deleting RID %d",
run.rid, exc_info=True)
"deleting RID %d", run.rid)
logger.debug("worker exception details", exc_info=True)
self.delete_cb(run.rid)
else:
self.delete_cb(run.rid)

View File

@ -21,10 +21,6 @@ class WorkerWatchdogTimeout(Exception):
pass
class WorkerException(Exception):
pass
class WorkerError(Exception):
pass
@ -60,13 +56,14 @@ class Worker:
else:
return None
async def _create_process(self):
async def _create_process(self, log_level):
await self.io_lock.acquire()
try:
if self.closed.is_set():
raise WorkerError("Attempting to create process after close")
self.process = await asyncio.create_subprocess_exec(
sys.executable, "-m", "artiq.master.worker_impl",
str(log_level),
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
finally:
self.io_lock.release()
@ -95,19 +92,26 @@ class Worker:
try:
await self._send(obj, cancellable=False)
except:
logger.warning("failed to send terminate command to worker"
logger.debug("failed to send terminate command to worker"
" (RID %s), killing", self.rid, exc_info=True)
try:
self.process.kill()
except ProcessLookupError:
pass
await self.process.wait()
return
try:
await asyncio.wait_for(self.process.wait(), term_timeout)
except asyncio.TimeoutError:
logger.warning("worker did not exit (RID %s), killing", self.rid)
logger.debug("worker did not exit by itself (RID %s), killing",
self.rid)
try:
self.process.kill()
except ProcessLookupError:
pass
await self.process.wait()
else:
logger.debug("worker exited gracefully (RID %s)", self.rid)
logger.debug("worker exited by itself (RID %s)", self.rid)
finally:
self.io_lock.release()
@ -163,10 +167,7 @@ class Worker:
return True
elif action == "pause":
return False
elif action == "exception":
raise WorkerException
del obj["action"]
if action == "create_watchdog":
elif action == "create_watchdog":
func = self.create_watchdog
elif action == "delete_watchdog":
func = self.delete_watchdog
@ -177,7 +178,7 @@ class Worker:
if getattr(func, "worker_pass_rid", False):
func = partial(func, self.rid)
try:
data = func(**obj)
data = func(*obj["args"], **obj["kwargs"])
reply = {"status": "ok", "data": data}
except:
reply = {"status": "failed",
@ -208,7 +209,7 @@ class Worker:
async def build(self, rid, pipeline_name, wd, expid, priority, timeout=15.0):
self.rid = rid
await self._create_process()
await self._create_process(expid["log_level"])
await self._worker_action(
{"action": "build",
"rid": rid,
@ -245,7 +246,7 @@ class Worker:
timeout)
async def examine(self, file, timeout=20.0):
await self._create_process()
await self._create_process(logging.WARNING)
r = dict()
def register(class_name, name, arguments):
r[class_name] = {"name": name, "arguments": arguments}

View File

@ -5,11 +5,11 @@ import os
import time
import re
import numpy
import numpy as np
import h5py
from artiq.protocols.sync_struct import Notifier
from artiq.protocols.pc_rpc import Client, BestEffortClient
from artiq.protocols.pc_rpc import AutoTarget, Client, BestEffortClient
logger = logging.getLogger(__name__)
@ -22,11 +22,16 @@ def _create_device(desc, device_mgr):
device_class = getattr(module, desc["class"])
return device_class(device_mgr, **desc["arguments"])
elif ty == "controller":
if desc["best_effort"]:
cl = BestEffortClient
if desc.get("best_effort", False):
cls = BestEffortClient
else:
cl = Client
return cl(desc["host"], desc["port"], desc["target_name"])
cls = Client
# Automatic target can be specified either by the absence of
# the target_name parameter, or a None value.
target_name = desc.get("target_name", None)
if target_name is None:
target_name = AutoTarget
return cls(desc["host"], desc["port"], target_name)
else:
raise ValueError("Unsupported type in device DB: " + ty)
@ -114,36 +119,53 @@ def get_last_rid():
_type_to_hdf5 = {
int: h5py.h5t.STD_I64BE,
float: h5py.h5t.IEEE_F64BE
float: h5py.h5t.IEEE_F64BE,
np.int8: h5py.h5t.STD_I8BE,
np.int16: h5py.h5t.STD_I16BE,
np.int32: h5py.h5t.STD_I32BE,
np.int64: h5py.h5t.STD_I64BE,
np.uint8: h5py.h5t.STD_U8BE,
np.uint16: h5py.h5t.STD_U16BE,
np.uint32: h5py.h5t.STD_U32BE,
np.uint64: h5py.h5t.STD_U64BE,
np.float16: h5py.h5t.IEEE_F16BE,
np.float32: h5py.h5t.IEEE_F32BE,
np.float64: h5py.h5t.IEEE_F64BE
}
def result_dict_to_hdf5(f, rd):
for name, data in rd.items():
if isinstance(data, list):
el_ty = type(data[0])
for d in data:
if type(d) != el_ty:
raise TypeError("All list elements must have the same"
" type for HDF5 output")
try:
el_ty_h5 = _type_to_hdf5[el_ty]
except KeyError:
raise TypeError("List element type {} is not supported for"
" HDF5 output".format(el_ty))
dataset = f.create_dataset(name, (len(data), ), el_ty_h5)
dataset[:] = data
elif isinstance(data, numpy.ndarray):
f.create_dataset(name, data=data)
flag = None
# beware: isinstance(True/False, int) == True
if isinstance(data, bool):
data = np.int8(data)
flag = "py_bool"
elif isinstance(data, int):
data = np.int64(data)
flag = "py_int"
if isinstance(data, np.ndarray):
dataset = f.create_dataset(name, data=data)
else:
ty = type(data)
if ty is str:
ty_h5 = "S{}".format(len(data))
data = data.encode()
else:
try:
ty_h5 = _type_to_hdf5[ty]
except KeyError:
raise TypeError("Type {} is not supported for HDF5 output"
.format(ty))
.format(ty)) from None
dataset = f.create_dataset(name, (), ty_h5)
dataset[()] = data
if flag is not None:
dataset.attrs[flag] = np.int8(1)
class DatasetManager:
def __init__(self, ddb):
@ -168,6 +190,7 @@ class DatasetManager:
try:
return self.local[key]
except KeyError:
pass
return self.ddb.get(key)
def write_hdf5(self, f):

View File

@ -1,7 +1,7 @@
import sys
import time
import os
import traceback
import logging
from artiq.protocols import pyon
from artiq.tools import file_import
@ -26,12 +26,9 @@ class ParentActionError(Exception):
pass
def make_parent_action(action, argnames, exception=ParentActionError):
argnames = argnames.split()
def parent_action(*args):
request = {"action": action}
for argname, arg in zip(argnames, args):
request[argname] = arg
def make_parent_action(action, exception=ParentActionError):
def parent_action(*args, **kwargs):
request = {"action": action, "args": args, "kwargs": kwargs}
put_object(request)
reply = get_object()
if "action" in reply:
@ -50,7 +47,7 @@ class LogForwarder:
def __init__(self):
self.buffer = ""
to_parent = staticmethod(make_parent_action("log", "message"))
to_parent = staticmethod(make_parent_action("log"))
def write(self, data):
self.buffer += data
@ -64,18 +61,18 @@ class LogForwarder:
class ParentDeviceDB:
get_device_db = make_parent_action("get_device_db", "")
get = make_parent_action("get_device", "key", KeyError)
get_device_db = make_parent_action("get_device_db")
get = make_parent_action("get_device", KeyError)
class ParentDatasetDB:
get = make_parent_action("get_dataset", "key", KeyError)
update = make_parent_action("update_dataset", "mod")
get = make_parent_action("get_dataset", KeyError)
update = make_parent_action("update_dataset")
class Watchdog:
_create = make_parent_action("create_watchdog", "t")
_delete = make_parent_action("delete_watchdog", "wid")
_create = make_parent_action("create_watchdog")
_delete = make_parent_action("delete_watchdog")
def __init__(self, t):
self.t = t
@ -91,15 +88,14 @@ set_watchdog_factory(Watchdog)
class Scheduler:
pause_noexc = staticmethod(make_parent_action("pause", ""))
pause_noexc = staticmethod(make_parent_action("pause"))
def pause(self):
if self.pause_noexc():
raise TerminationRequested
submit = staticmethod(make_parent_action("scheduler_submit",
"pipeline_name expid priority due_date flush"))
cancel = staticmethod(make_parent_action("scheduler_cancel", "rid"))
submit = staticmethod(make_parent_action("scheduler_submit"))
cancel = staticmethod(make_parent_action("scheduler_cancel"))
def set_run_info(self, pipeline_name, expid, priority):
self.pipeline_name = pipeline_name
@ -120,22 +116,21 @@ def get_exp(file, class_name):
return getattr(module, class_name)
register_experiment = make_parent_action("register_experiment",
"class_name name arguments")
register_experiment = make_parent_action("register_experiment")
class ExamineDeviceMgr:
get_device_db = make_parent_action("get_device_db", "")
get_device_db = make_parent_action("get_device_db")
def get(self, name):
def get(name):
return None
class DummyDatasetMgr:
def set(self, key, value, broadcast=False, persist=False, save=True):
def set(key, value, broadcast=False, persist=False, save=True):
return None
def get(self, key):
def get(key):
pass
@ -158,7 +153,9 @@ def examine(device_mgr, dataset_mgr, file):
def main():
sys.stdout = sys.stderr = LogForwarder()
sys.stdout = LogForwarder()
sys.stderr = LogForwarder()
logging.basicConfig(level=int(sys.argv[1]))
start_time = None
rid = None
@ -211,15 +208,15 @@ def main():
f.close()
put_object({"action": "completed"})
elif action == "examine":
examine(ExamineDeviceMgr(), DummyDatasetMgr(), obj["file"])
examine(ExamineDeviceMgr, DummyDatasetMgr, obj["file"])
put_object({"action": "completed"})
elif action == "terminate":
break
except:
traceback.print_exc()
put_object({"action": "exception"})
logging.error("Worker terminating with exception", exc_info=True)
finally:
device_mgr.close_devices()
if __name__ == "__main__":
main()

133
artiq/protocols/logging.py Normal file
View File

@ -0,0 +1,133 @@
import asyncio
import logging
from artiq.protocols.asyncio_server import AsyncioServer
from artiq.tools import TaskObject, workaround_asyncio263
logger = logging.getLogger(__name__)
_fwd_logger = logging.getLogger("fwd")
def log_with_name(name, *args, **kwargs):
_fwd_logger.name = name
_fwd_logger.log(*args, **kwargs)
_name_to_level = {
"CRITICAL": logging.CRITICAL,
"ERROR": logging.ERROR,
"WARN": logging.WARNING,
"WARNING": logging.WARNING,
"INFO": logging.INFO,
"DEBUG": logging.DEBUG,
}
def parse_log_message(msg):
for name, level in _name_to_level.items():
if msg.startswith(name + ":"):
remainder = msg[len(name) + 1:]
try:
idx = remainder.index(":")
except:
continue
return level, remainder[:idx], remainder[idx+1:]
return logging.INFO, "print", msg
_init_string = b"ARTIQ logging\n"
class Server(AsyncioServer):
"""Remote logging TCP server.
Takes one log entry per line, in the format:
source:levelno:name:message
"""
async def _handle_connection_cr(self, reader, writer):
try:
line = await reader.readline()
if line != _init_string:
return
while True:
line = await reader.readline()
if not line:
break
try:
line = line.decode()
except:
return
line = line[:-1]
linesplit = line.split(":", 3)
if len(linesplit) != 4:
logger.warning("received improperly formatted message, "
"dropping connection")
return
source, level, name, message = linesplit
try:
level = int(level)
except:
logger.warning("received improperly formatted level, "
"dropping connection")
return
log_with_name(name, level, message,
extra={"source": source})
finally:
writer.close()
class SourceFilter:
def __init__(self, local_level, local_source):
self.local_level = local_level
self.local_source = local_source
def filter(self, record):
if not hasattr(record, "source"):
record.source = self.local_source
if record.source == self.local_source:
return record.levelno >= self.local_level
else:
# log messages that are forwarded from a source have already
# been filtered, and may have a level below the local level.
return True
class LogForwarder(logging.Handler, TaskObject):
def __init__(self, host, port, reconnect_timer=5.0, queue_size=1000,
**kwargs):
logging.Handler.__init__(self, **kwargs)
self.host = host
self.port = port
self.setFormatter(logging.Formatter(
"%(name)s:%(message)s"))
self._queue = asyncio.Queue(queue_size)
self.reconnect_timer = reconnect_timer
def emit(self, record):
message = self.format(record)
for part in message.split("\n"):
part = "{}:{}:{}".format(record.source, record.levelno, part)
try:
self._queue.put_nowait(part)
except asyncio.QueueFull:
break
async def _do(self):
while True:
try:
reader, writer = await asyncio.open_connection(self.host,
self.port)
writer.write(_init_string)
while True:
message = await self._queue.get() + "\n"
writer.write(message.encode())
await workaround_asyncio263()
await writer.drain()
except asyncio.CancelledError:
return
except:
await asyncio.sleep(self.reconnect_timer)
finally:
writer.close()

View File

@ -27,6 +27,12 @@ from artiq.protocols.asyncio_server import AsyncioServer as _AsyncioServer
logger = logging.getLogger(__name__)
class AutoTarget:
"""Use this as target value in clients for them to automatically connect
to the target exposed by the server. Servers must have only one target."""
pass
class RemoteError(Exception):
"""Raised when a RPC failed or raised an exception on the remote (server)
side."""
@ -42,6 +48,20 @@ class IncompatibleServer(Exception):
_init_string = b"ARTIQ pc_rpc\n"
def _validate_target_name(target_name, target_names):
if target_name is AutoTarget:
if len(target_names) > 1:
raise ValueError("Server has multiple targets: " +
" ".join(sorted(target_names)))
else:
target_name = target_names[0]
elif target_name not in target_names:
raise IncompatibleServer(
"valid target name(s): " +
" ".join(sorted(target_names)))
return target_name
class Client:
"""This class proxies the methods available on the server so that they
can be used as if they were local methods.
@ -67,11 +87,13 @@ class Client:
:param port: TCP port to use.
:param target_name: Target name to select. ``IncompatibleServer`` is
raised if the target does not exist.
Use ``AutoTarget`` for automatic selection if the server has only one
target.
Use ``None`` to skip selecting a target. The list of targets can then
be retrieved using ``get_rpc_id`` and then one can be selected later
using ``select_rpc_target``.
"""
def __init__(self, host, port, target_name):
def __init__(self, host, port, target_name=AutoTarget):
self.__socket = socket.create_connection((host, port))
try:
@ -89,8 +111,7 @@ class Client:
def select_rpc_target(self, target_name):
"""Selects a RPC target by name. This function should be called
exactly once if the object was created with ``target_name=None``."""
if target_name not in self.__target_names:
raise IncompatibleServer
target_name = _validate_target_name(target_name, self.__target_names)
self.__socket.sendall((target_name + "\n").encode())
def get_rpc_id(self):
@ -180,8 +201,7 @@ class AsyncioClient:
"""Selects a RPC target by name. This function should be called
exactly once if the connection was created with ``target_name=None``.
"""
if target_name not in self.__target_names:
raise IncompatibleServer
target_name = _validate_target_name(target_name, self.__target_names)
self.__writer.write((target_name + "\n").encode())
def get_rpc_id(self):
@ -259,7 +279,8 @@ class BestEffortClient:
except:
logger.warning("first connection attempt to %s:%d[%s] failed, "
"retrying in the background",
self.__host, self.__port, self.__target_name)
self.__host, self.__port, self.__target_name,
exc_info=True)
self.__start_conretry()
else:
self.__conretry_thread = None
@ -273,9 +294,9 @@ class BestEffortClient:
(self.__host, self.__port), timeout)
self.__socket.sendall(_init_string)
server_identification = self.__recv()
if self.__target_name not in server_identification["targets"]:
raise IncompatibleServer
self.__socket.sendall((self.__target_name + "\n").encode())
target_name = _validate_target_name(self.__target_name,
server_identification["targets"])
self.__socket.sendall((target_name + "\n").encode())
def __start_conretry(self):
self.__conretry_thread = threading.Thread(target=self.__conretry)

View File

@ -132,7 +132,10 @@ class _Encoder:
return r
def encode(self, x):
return getattr(self, "encode_" + _encode_map[type(x)])(x)
ty = _encode_map.get(type(x), None)
if ty is None:
raise TypeError(repr(x) + " is not PYON serializable")
return getattr(self, "encode_" + ty)(x)
def encode(x, pretty=False):
@ -145,6 +148,7 @@ def encode(x, pretty=False):
def _nparray(shape, dtype, data):
a = numpy.frombuffer(base64.b64decode(data), dtype=dtype)
a = a.copy()
return a.reshape(shape)

View File

@ -16,6 +16,7 @@ from functools import partial
from artiq.protocols import pyon
from artiq.protocols.asyncio_server import AsyncioServer
from artiq.tools import workaround_asyncio263
_init_string = b"ARTIQ sync_struct\n"
@ -233,10 +234,11 @@ class Publisher(AsyncioServer):
line = await queue.get()
writer.write(line)
# raise exception on connection error
await workaround_asyncio263()
await writer.drain()
finally:
self._recipients[notifier_name].remove(queue)
except ConnectionResetError:
except (ConnectionResetError, BrokenPipeError):
# subscribers disconnecting are a normal occurence
pass
finally:

25
artiq/test/h5types.py Normal file
View File

@ -0,0 +1,25 @@
import unittest
import h5py
import numpy as np
from artiq.master.worker_db import result_dict_to_hdf5
class TypesCase(unittest.TestCase):
def test_types(self):
d = {
"bool": True,
"int": 42,
"float": 42.0,
"string": "abcdef",
}
for size in 8, 16, 32, 64:
d["i"+str(size)] = getattr(np, "int" + str(size))(42)
d["u"+str(size)] = getattr(np, "uint" + str(size))(42)
for size in 16, 32, 64:
d["f"+str(size)] = getattr(np, "float" + str(size))(42)
with h5py.File("h5types.h5", "w") as f:
result_dict_to_hdf5(f, d)

View File

@ -17,12 +17,12 @@ test_object = [5, 2.1, None, True, False,
class RPCCase(unittest.TestCase):
def _run_server_and_test(self, test):
def _run_server_and_test(self, test, *args):
# running this file outside of unittest starts the echo server
with subprocess.Popen([sys.executable,
sys.modules[__name__].__file__]) as proc:
try:
test()
test(*args)
finally:
try:
proc.wait(timeout=1)
@ -30,12 +30,12 @@ class RPCCase(unittest.TestCase):
proc.kill()
raise
def _blocking_echo(self):
def _blocking_echo(self, target):
for attempt in range(100):
time.sleep(.2)
try:
remote = pc_rpc.Client(test_address, test_port,
"test")
target)
except ConnectionRefusedError:
pass
else:
@ -50,14 +50,17 @@ class RPCCase(unittest.TestCase):
remote.close_rpc()
def test_blocking_echo(self):
self._run_server_and_test(self._blocking_echo)
self._run_server_and_test(self._blocking_echo, "test")
async def _asyncio_echo(self):
def test_blocking_echo_autotarget(self):
self._run_server_and_test(self._blocking_echo, pc_rpc.AutoTarget)
async def _asyncio_echo(self, target):
remote = pc_rpc.AsyncioClient()
for attempt in range(100):
await asyncio.sleep(.2)
try:
await remote.connect_rpc(test_address, test_port, "test")
await remote.connect_rpc(test_address, test_port, target)
except ConnectionRefusedError:
pass
else:
@ -71,16 +74,19 @@ class RPCCase(unittest.TestCase):
finally:
remote.close_rpc()
def _loop_asyncio_echo(self):
def _loop_asyncio_echo(self, target):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(self._asyncio_echo())
loop.run_until_complete(self._asyncio_echo(target))
finally:
loop.close()
def test_asyncio_echo(self):
self._run_server_and_test(self._loop_asyncio_echo)
self._run_server_and_test(self._loop_asyncio_echo, "test")
def test_asyncio_echo_autotarget(self):
self._run_server_and_test(self._loop_asyncio_echo, pc_rpc.AutoTarget)
class FireAndForgetCase(unittest.TestCase):

View File

@ -1,4 +1,5 @@
import unittest
import logging
import asyncio
import sys
import os
@ -32,6 +33,7 @@ class BackgroundExperiment(EnvExperiment):
def _get_expid(name):
return {
"log_level": logging.WARNING,
"file": sys.modules[__name__].__file__,
"class_name": name,
"arguments": dict()

View File

@ -17,15 +17,17 @@ def write_test_data(test_dict):
for key, value in enumerate(test_values):
test_dict[key] = value
test_dict[1.5] = 1.5
test_dict["array"] = []
test_dict["array"].append(42)
test_dict["array"].insert(1, 1)
test_dict["list"] = []
test_dict["list"].append(42)
test_dict["list"].insert(1, 1)
test_dict[100] = 0
test_dict[100] = 1
test_dict[101] = 1
test_dict.pop(101)
test_dict[102] = 1
del test_dict[102]
test_dict["array"] = np.zeros(1)
test_dict["array"][0] = 10
test_dict["finished"] = True

View File

@ -1,4 +1,5 @@
import unittest
import logging
import asyncio
import sys
import os
@ -64,6 +65,7 @@ async def _call_worker(worker, expid):
def _run_experiment(class_name):
expid = {
"log_level": logging.WARNING,
"file": sys.modules[__name__].__file__,
"class_name": class_name,
"arguments": dict()
@ -85,7 +87,7 @@ class WorkerCase(unittest.TestCase):
_run_experiment("SimpleExperiment")
def test_exception(self):
with self.assertRaises(WorkerException):
with self.assertRaises(WorkerError):
_run_experiment("ExceptionTermination")
def test_watchdog_no_timeout(self):

View File

@ -49,14 +49,16 @@ def short_format(v):
if v is None:
return "None"
t = type(v)
if np.issubdtype(t, int) or np.issubdtype(t, float):
if t is bool or np.issubdtype(t, int) or np.issubdtype(t, float):
return str(v)
elif t is str:
return "\"" + elide(v, 15) + "\""
return "\"" + elide(v, 50) + "\""
else:
r = t.__name__
if t is list or t is dict or t is set:
r += " ({})".format(len(v))
if t is np.ndarray:
r += " " + str(np.shape(v))
return r
@ -175,3 +177,9 @@ class Condition:
for fut in self._waiters:
if not fut.done():
fut.set_result(False)
# See: https://github.com/python/asyncio/issues/263
@asyncio.coroutine
def workaround_asyncio263():
yield

View File

@ -1,17 +0,0 @@
Uploading conda packages (Python 3.5)
=====================================
Preparing:
1. [Install miniconda][miniconda]
2. `conda update -q conda`
3. `conda install conda-build jinja2 anaconda`
4. `conda create -q -n py35 python=3.5`
5. `conda config --add channels https://conda.anaconda.org/m-labs/channel/dev`
Building:
1. `conda build pkgname --python 3.5`; this command displays a path to the freshly built package
2. `anaconda upload <package> -c main -c dev`
[miniconda]: http://conda.pydata.org/docs/install/quick.html#linux-miniconda-install

View File

@ -1,2 +0,0 @@
"%PYTHON%" setup.py install
if errorlevel 1 exit 1

View File

@ -1,3 +0,0 @@
#!/bin/bash
$PYTHON setup.py install

View File

@ -1,36 +0,0 @@
package:
name: aiohttp
version: "0.17.2"
source:
fn: aiohttp-0.17.2.tar.gz
url: https://pypi.python.org/packages/source/a/aiohttp/aiohttp-0.17.2.tar.gz
md5: 7640928fd4b5c1ccf1f8bcad276d39d6
build:
number: 0
requirements:
build:
- python
- setuptools
- chardet
run:
- python
- chardet
test:
# Python imports
imports:
- aiohttp
requires:
- chardet
- gunicorn # [not win]
- nose
about:
home: https://github.com/KeepSafe/aiohttp/
license: Apache Software License
summary: 'http client/server for asyncio'

View File

@ -0,0 +1,26 @@
#!/bin/bash
BUILD_SETTINGS_FILE=$HOME/.m-labs/build_settings.sh
[ -f $BUILD_SETTINGS_FILE ] && . $BUILD_SETTINGS_FILE
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/kc705
mkdir -p $SOC_PREFIX/nist_qc1
SOC_ROOT=$PWD/soc
# build bitstream
(cd $MSCDIR; $PYTHON make.py -X $SOC_ROOT -t artiq_kc705 $MISOC_EXTRA_VIVADO_CMDLINE build-bitstream)
cp $MSCDIR/build/artiq_kc705-nist_qc1-kc705.bit $SOC_PREFIX/
wget http://sionneau.net/artiq/binaries/kc705/flash_proxy/bscan_spi_kc705.bit
mv bscan_spi_kc705.bit $SOC_PREFIX/
# build BIOS
(cd $MSCDIR; $PYTHON make.py -X $SOC_ROOT -t artiq_kc705 build-headers build-bios)
cp $MSCDIR/software/bios/bios.bin $SOC_PREFIX/
# build runtime
make -C soc/runtime clean runtime.fbi
cp soc/runtime/runtime.fbi $SOC_PREFIX/nist_qc1/

View File

@ -0,0 +1,27 @@
package:
name: artiq-kc705-nist_qc1
version: {{ environ.get("GIT_DESCRIBE_TAG", "") }}
source:
git_url: https://github.com/m-labs/artiq
git_tag: master
build:
noarch_python: true
number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}
string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }}
requirements:
build:
# We don't get meaningful GIT_DESCRIBE_* values until before conda installs build dependencies.
- artiq 0.0
- migen 0.0
- llvm-or1k
- binutils-or1k-linux
run:
- artiq 0.0
about:
home: http://m-labs.hk/artiq
license: GPL
summary: 'Bitstream, BIOS and runtime for NIST_QC1 on the KC705 board'

View File

@ -0,0 +1,26 @@
#!/bin/bash
BUILD_SETTINGS_FILE=$HOME/.m-labs/build_settings.sh
[ -f $BUILD_SETTINGS_FILE ] && . $BUILD_SETTINGS_FILE
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/kc705
mkdir -p $SOC_PREFIX/nist_qc2
SOC_ROOT=$PWD/soc
# build bitstream
(cd $MSCDIR; $PYTHON make.py -X $SOC_ROOT -t artiq_kc705 -s NIST_QC2 $MISOC_EXTRA_VIVADO_CMDLINE build-bitstream)
cp $MSCDIR/build/artiq_kc705-nist_qc2-kc705.bit $SOC_PREFIX/
wget http://sionneau.net/artiq/binaries/kc705/flash_proxy/bscan_spi_kc705.bit
mv bscan_spi_kc705.bit $SOC_PREFIX/
# build BIOS
(cd $MSCDIR; $PYTHON make.py -X $SOC_ROOT -t artiq_kc705 -s NIST_QC2 build-headers build-bios)
cp $MSCDIR/software/bios/bios.bin $SOC_PREFIX/
# build runtime
make -C soc/runtime clean runtime.fbi
cp soc/runtime/runtime.fbi $SOC_PREFIX/nist_qc2/

View File

@ -0,0 +1,27 @@
package:
name: artiq-kc705-nist_qc2
version: {{ environ.get("GIT_DESCRIBE_TAG", "") }}
source:
git_url: https://github.com/m-labs/artiq
git_tag: master
build:
noarch_python: true
number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}
string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }}
requirements:
build:
# We don't get meaningful GIT_DESCRIBE_* values until before conda installs build dependencies.
- artiq 0.0
- migen 0.0
- llvm-or1k
- binutils-or1k-linux
run:
- artiq 0.0
about:
home: http://m-labs.hk/artiq
license: GPL
summary: 'Bitstream, BIOS and runtime for NIST_QC2 on the KC705 board'

View File

@ -0,0 +1,26 @@
#!/bin/bash
BUILD_SETTINGS_FILE=$HOME/.m-labs/build_settings.sh
[ -f $BUILD_SETTINGS_FILE ] && . $BUILD_SETTINGS_FILE
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/pipistrello
mkdir -p $SOC_PREFIX
SOC_ROOT=$PWD/soc
# build bitstream
(cd $MSCDIR; $PYTHON make.py -X $SOC_ROOT -t artiq_pipistrello $MISOC_EXTRA_ISE_CMDLINE build-bitstream)
cp $MSCDIR/build/artiq_pipistrello-nist_qc1-pipistrello.bit $SOC_PREFIX/
wget https://people.phys.ethz.ch/~robertjo/bscan_spi_lx45_csg324.bit
mv bscan_spi_lx45_csg324.bit $SOC_PREFIX/
# build BIOS
(cd $MSCDIR; $PYTHON make.py -X $SOC_ROOT -t artiq_pipistrello build-headers build-bios)
cp $MSCDIR/software/bios/bios.bin $SOC_PREFIX/
# build runtime
make -C soc/runtime clean runtime.fbi
cp soc/runtime/runtime.fbi $SOC_PREFIX/

View File

@ -0,0 +1,27 @@
package:
name: artiq-pipistrello-nist_qc1
version: {{ environ.get("GIT_DESCRIBE_TAG", "") }}
source:
git_url: https://github.com/m-labs/artiq
git_tag: master
build:
noarch_python: true
number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}
string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }}
requirements:
build:
# We don't get meaningful GIT_DESCRIBE_* values until before conda installs build dependencies.
- artiq 0.0
- migen 0.0
- llvm-or1k
- binutils-or1k-linux
run:
- artiq 0.0
about:
home: http://m-labs.hk/artiq
license: GPL
summary: 'Bitstream, BIOS and runtime for NIST_QC1 on the Pipistrello board'

View File

@ -1,64 +1,15 @@
#!/bin/bash
BUILD_SETTINGS_FILE=$HOME/.mlabs/build_settings.sh
if [ -f $BUILD_SETTINGS_FILE ]
then
source $BUILD_SETTINGS_FILE
fi
ARTIQ_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq
$PYTHON setup.py install --single-version-externally-managed --record=record.txt
git clone --recursive https://github.com/m-labs/misoc
export MSCDIR=$SRC_DIR/misoc
ARTIQ_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq
BIN_PREFIX=$ARTIQ_PREFIX/binaries/
mkdir -p $ARTIQ_PREFIX/misc
mkdir -p $BIN_PREFIX/kc705 $BIN_PREFIX/pipistrello
# build for KC705 NIST_QC1
cd $SRC_DIR/misoc; $PYTHON make.py -X ../soc -t artiq_kc705 build-headers build-bios; cd -
make -C soc/runtime clean runtime.fbi
[ "$BUILD_SOC" != "0" ] && (cd $SRC_DIR/misoc; $PYTHON make.py -X ../soc -t artiq_kc705 $MISOC_EXTRA_VIVADO_CMDLINE build-bitstream)
# install KC705 NIST_QC1 binaries
mkdir -p $BIN_PREFIX/kc705/nist_qc1
cp soc/runtime/runtime.fbi $BIN_PREFIX/kc705/nist_qc1/
cp $SRC_DIR/misoc/software/bios/bios.bin $BIN_PREFIX/kc705/
[ "$BUILD_SOC" != "0" ] && cp $SRC_DIR/misoc/build/artiq_kc705-nist_qc1-kc705.bit $BIN_PREFIX/kc705/
wget http://sionneau.net/artiq/binaries/kc705/flash_proxy/bscan_spi_kc705.bit
mv bscan_spi_kc705.bit $BIN_PREFIX/kc705/
# build for Pipistrello
cd $SRC_DIR/misoc; $PYTHON make.py -X ../soc -t artiq_pipistrello build-headers build-bios; cd -
make -C soc/runtime clean runtime.fbi
[ "$BUILD_SOC" != "0" ] && (cd $SRC_DIR/misoc; $PYTHON make.py -X ../soc -t artiq_pipistrello $MISOC_EXTRA_ISE_CMDLINE build-bitstream)
# install Pipistrello binaries
cp soc/runtime/runtime.fbi $BIN_PREFIX/pipistrello/
cp $SRC_DIR/misoc/software/bios/bios.bin $BIN_PREFIX/pipistrello/
[ "$BUILD_SOC" != "0" ] && cp $SRC_DIR/misoc/build/artiq_pipistrello-nist_qc1-pipistrello.bit $BIN_PREFIX/pipistrello/
wget https://people.phys.ethz.ch/~robertjo/bscan_spi_lx45_csg324.bit
mv bscan_spi_lx45_csg324.bit $BIN_PREFIX/pipistrello/
# build for KC705 NIST_QC2
cd $SRC_DIR/misoc; $PYTHON make.py -X ../soc -t artiq_kc705 -s NIST_QC2 build-headers; cd -
make -C soc/runtime clean runtime.fbi
[ "$BUILD_SOC" != "0" ] && (cd $SRC_DIR/misoc; $PYTHON make.py -X ../soc -t artiq_kc705 -s NIST_QC2 $MISOC_EXTRA_VIVADO_CMDLINE build-bitstream)
# install KC705 NIST_QC2 binaries
mkdir -p $BIN_PREFIX/kc705/nist_qc2
cp soc/runtime/runtime.fbi $BIN_PREFIX/kc705/nist_qc2/
[ "$BUILD_SOC" != "0" ] && cp $SRC_DIR/misoc/build/artiq_kc705-nist_qc2-kc705.bit $BIN_PREFIX/kc705/
# install scripts
cp artiq/frontend/artiq_flash.sh $PREFIX/bin
# misc
# install udev rules
mkdir -p $ARTIQ_PREFIX/misc
cp misc/99-papilio.rules $ARTIQ_PREFIX/misc/
cp misc/99-kc705.rules $ARTIQ_PREFIX/misc/

View File

@ -7,7 +7,9 @@ source:
git_tag: master
build:
noarch_python: true
number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}
string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }}
entry_points:
- artiq_client = artiq.frontend.artiq_client:main
- artiq_compile = artiq.frontend.artiq_compile:main
@ -58,8 +60,7 @@ test:
imports:
- artiq
about:
home: http://m-labs.hk/artiq
license: 3-clause BSD
license: GPL
summary: 'ARTIQ (Advanced Real-Time Infrastructure for Quantum physics) is a next-generation control system for quantum information experiments. It is being developed in partnership with the Ion Storage Group at NIST, and its applicability reaches beyond ion trapping.'

View File

@ -1,8 +0,0 @@
binutils-or1k-linux
===================
To build this package on Windows:
* Install cygwin
* Install the following packages: gcc-core g++-core make texinfo patch
* Run cygwin terminal and execute $ conda build binutils-or1k-linux

View File

@ -1,10 +0,0 @@
FOR /F "tokens=* USEBACKQ" %%F IN (`cygpath -u %PREFIX%`) DO (
SET var=%%F
)
set PREFIX=%var%
FOR /F "tokens=* USEBACKQ" %%F IN (`cygpath -u %RECIPE_DIR%`) DO (
SET var=%%F
)
set RECIPE_DIR=%var%
sh %RECIPE_DIR%/build.sh
if errorlevel 1 exit 1

View File

@ -1,6 +0,0 @@
patch -p1 < $RECIPE_DIR/../../misc/binutils-2.25.1-or1k-R_PCREL-pcrel_offset.patch
mkdir build
cd build
../configure --target=or1k-linux --prefix=$PREFIX
make -j2
make install

View File

@ -1,20 +0,0 @@
package:
name: binutils-or1k-linux
version: 2.25.1
source:
fn: binutils-2.25.1.tar.bz2
url: https://ftp.gnu.org/gnu/binutils/binutils-2.25.1.tar.bz2
sha256: b5b14added7d78a8d1ca70b5cb75fef57ce2197264f4f5835326b0df22ac9f22
build:
number: 0
requirements:
build:
- system # [not win]
about:
home: https://www.gnu.org/software/binutils/
license: GPL
summary: 'A set of programming tools for creating and managing binary programs, object files, libraries, profile data, and assembly source code.'

View File

@ -1,2 +0,0 @@
"%PYTHON%" setup.py install
if errorlevel 1 exit 1

View File

@ -1 +0,0 @@
$PYTHON setup.py install

View File

@ -1,33 +0,0 @@
package:
name: chardet
version: 2.2.1
source:
fn: chardet-2.2.1.tar.gz
url: https://pypi.python.org/packages/source/c/chardet/chardet-2.2.1.tar.gz
md5: 4a758402eaefd0331bdedc7ecb6f452c
build:
entry_points:
- chardetect = chardet.chardetect:main
number: 0
requirements:
build:
- python
- setuptools
run:
- python
test:
# Python imports
imports:
- chardet
commands:
- chardetect run_test.py
about:
home: https://github.com/chardet/chardet
license: GNU Library or Lesser General Public License (LGPL)

View File

@ -1 +0,0 @@
%PYTHON% setup.py install

View File

@ -1 +0,0 @@
$PYTHON setup.py install

View File

@ -1,30 +0,0 @@
package:
name: dateutil
version: 2.4.2
source:
fn: python-dateutil-2.4.2.tar.gz
url: https://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.4.2.tar.gz
md5: 4ef68e1c485b09e9f034e10473e5add2
build:
number: 0
requirements:
build:
- python
- setuptools
- six >=1.5
run:
- python
- six >=1.5
test:
imports:
- dateutil
- dateutil.zoneinfo
about:
home: https://dateutil.readthedocs.org
license: BSD
summary: 'Extensions to the standard Python datetime module'

View File

@ -1,5 +0,0 @@
#!/bin/bash
make -C $SRC_DIR/tools flterm
mkdir -p $PREFIX/bin
cp $SRC_DIR/tools/flterm $PREFIX/bin/

View File

@ -1,12 +0,0 @@
package:
name: flterm
version: 0
source:
git_url: https://github.com/m-labs/misoc
git_tag: master
about:
home: https://github.com/m-labs/misoc/blob/master/tools/flterm.c
license: 3-clause BSD
summary: 'Serial terminal to connect to MiSoC uart.'

View File

@ -1,20 +0,0 @@
mkdir build
cd build
REM Configure step
if "%ARCH%"=="32" (
set CMAKE_GENERATOR=Visual Studio 12 2013
) else (
set CMAKE_GENERATOR=Visual Studio 12 2013 Win64
)
set CMAKE_GENERATOR_TOOLSET=v120_xp
cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%PREFIX% -DSTDCALL=OFF -DCMAKE_PREFIX_PATH=$PREFIX %SRC_DIR%
if errorlevel 1 exit 1
REM Build step
cmake --build .
if errorlevel 1 exit 1
REM Install step
cmake --build . --target install
if errorlevel 1 exit 1
REM Hack to help pygit2 to find libgit2
mkdir %PREFIX%\Scripts
copy "%PREFIX%\bin\git2.dll" "%PREFIX%\Scripts\"

View File

@ -1,7 +0,0 @@
#!/bin/bash
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_PREFIX_PATH=$PREFIX
make -j2
make install

View File

@ -1,27 +0,0 @@
package:
name: libgit2
version: 0.22.3
source:
git_url: https://github.com/libgit2/libgit2
git_tag: v0.22.3
build:
number: 1
requirements:
build:
- system # [linux]
- cmake # [linux]
- openssl
- libssh2
- zlib
run:
- openssl
- zlib
- libssh2
about:
home: https://libgit2.github.com/
license: GPLv2 with a special Linking Exception
summary: 'libgit2 is a portable, pure C implementation of the Git core methods provided as a re-entrant linkable library with a solid API, allowing you to write native speed custom Git applications in any language with bindings.'

View File

@ -1,17 +0,0 @@
mkdir build
cd build
REM Configure step
if "%ARCH%"=="32" (
set CMAKE_GENERATOR=Visual Studio 12 2013
) else (
set CMAKE_GENERATOR=Visual Studio 12 2013 Win64
)
set CMAKE_GENERATOR_TOOLSET=v120_xp
cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%PREFIX% -DOPENSSL_ROOT_DIR=%PREFIX%\Library -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DCMAKE_PREFIX_PATH=$PREFIX %SRC_DIR%
if errorlevel 1 exit 1
REM Build step
cmake --build .
if errorlevel 1 exit 1
REM Install step
cmake --build . --target install
if errorlevel 1 exit 1

View File

@ -1,7 +0,0 @@
#!/bin/bash
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX -DOPENSSL_ROOT_DIR=$PREFIX -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DCMAKE_PREFIX_PATH=$PREFIX
make -j2
make install

View File

@ -1,23 +0,0 @@
package:
name: libssh2
version: 1.6.0
source:
git_url: https://github.com/libssh2/libssh2
git_tag: libssh2-1.6.0
build:
number: 1
requirements:
build:
- system # [linux]
- cmake # [linux]
- openssl
run:
- openssl
about:
home: http://www.libssh2.org/
license: BSD
summary: 'libssh2 is a client-side C library implementing the SSH2 protocol'

View File

@ -1,2 +0,0 @@
"%PYTHON%" setup.py install
if errorlevel 1 exit 1

View File

@ -1 +0,0 @@
$PYTHON setup.py install

View File

@ -1,27 +0,0 @@
package:
name: lit
version: 0.4.1
source:
fn: lit-0.4.1.tar.gz
url: https://pypi.python.org/packages/source/l/lit/lit-0.4.1.tar.gz
md5: ea6f00470e1bf7ed9e4edcff0f650fe6
build:
number: 0
requirements:
build:
- python
- setuptools
run:
- python
test:
commands:
- lit --version
about:
home: http://llvm.org/docs/CommandGuide/lit.html
license: MIT

View File

@ -1,25 +0,0 @@
mkdir build
cd build
set BUILD_CONFIG=Release
REM Configure step
if "%ARCH%"=="32" (
set CMAKE_GENERATOR=Visual Studio 12 2013
) else (
set CMAKE_GENERATOR=Visual Studio 12 2013 Win64
)
set CMAKE_GENERATOR_TOOLSET=v120_xp
@rem Reduce build times and package size by removing unused stuff
set CMAKE_CUSTOM=-DLLVM_TARGETS_TO_BUILD="OR1K;X86" -DLLVM_INCLUDE_TESTS=OFF ^
-DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF ^
-DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF ^
-DLLVM_ENABLE_ASSERTIONS=ON
cmake -G "%CMAKE_GENERATOR%" -T "%CMAKE_GENERATOR_TOOLSET%" ^
-DCMAKE_BUILD_TYPE="%BUILD_CONFIG%" -DCMAKE_PREFIX_PATH=%LIBRARY_PREFIX% ^
-DCMAKE_INSTALL_PREFIX:PATH=%LIBRARY_PREFIX% %CMAKE_CUSTOM% %SRC_DIR%
if errorlevel 1 exit 1
REM Build step
cmake --build . --config "%BUILD_CONFIG%"
if errorlevel 1 exit 1
REM Install step
cmake --build . --config "%BUILD_CONFIG%" --target install
if errorlevel 1 exit 1

View File

@ -1,10 +0,0 @@
#!/bin/bash
cd tools
git clone https://github.com/openrisc/clang-or1k clang
cd ..
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX -DLLVM_TARGETS_TO_BUILD="OR1K;X86" -DCMAKE_BUILD_TYPE=Rel -DLLVM_ENABLE_ASSERTIONS=ON
make -j2
make install

View File

@ -1,22 +0,0 @@
package:
name: llvmdev-or1k
version: "3.5.0"
source:
git_url: https://github.com/openrisc/llvm-or1k
git_tag: master
build:
number: 5
requirements:
build:
- system [linux]
- cmake [linux]
run:
- system [linux]
about:
home: http://llvm.org/
license: Open Source (http://llvm.org/releases/3.5.0/LICENSE.TXT)
summary: Development headers and libraries for LLVM

View File

@ -1,8 +0,0 @@
@rem Let CMake know about the LLVM install path, for find_package()
set CMAKE_PREFIX_PATH=%LIBRARY_PREFIX%
@rem Ensure there are no build leftovers (CMake can complain)
if exist ffi\build rmdir /S /Q ffi\build
%PYTHON% setup.py install
if errorlevel 1 exit 1

View File

@ -1,3 +0,0 @@
#!/bin/bash
$PYTHON setup.py install

View File

@ -1,27 +0,0 @@
package:
name: llvmlite-artiq
version: "0.5.1"
source:
git_url: https://github.com/m-labs/llvmlite
git_tag: artiq
requirements:
build:
- python
- llvmdev-or1k
- setuptools
run:
- python
build:
number: 5
test:
imports:
- llvmlite_artiq
- llvmlite_artiq.binding
about:
home: https://pypi.python.org/pypi/llvmlite/
license: BSD

View File

@ -1,8 +0,0 @@
"%PYTHON%" setup.py install
if errorlevel 1 exit 1
:: Add more build steps here, if they are necessary.
:: See
:: http://docs.continuum.io/conda/build.html
:: for a list of environment variables that are set during the build process.

View File

@ -1,3 +0,0 @@
#!/bin/bash
$PYTHON setup.py install

View File

@ -1,26 +0,0 @@
package:
name: prettytable
version: !!str 0.7.2
source:
fn: prettytable-0.7.2.tar.bz2
url: https://pypi.python.org/packages/source/P/PrettyTable/prettytable-0.7.2.tar.bz2
md5: 760dc900590ac3c46736167e09fa463a
requirements:
build:
- python
- setuptools
run:
- python
test:
imports:
- prettytable
about:
home: http://code.google.com/p/prettytable/
license: BSD License
summary: 'A simple Python library for easily displaying tabular data in a visually appealing ASCII table format.'

View File

@ -1,2 +0,0 @@
"%PYTHON%" setup.py build
"%PYTHON%" setup.py install

View File

@ -1,2 +0,0 @@
$PYTHON setup.py build
$PYTHON setup.py install

View File

@ -1,22 +0,0 @@
package:
name: pydaqmx
version: "1.3.1"
source:
git_url: https://github.com/clade/pydaqmx
git_tag: master
build:
number: 0
requirements:
build:
- python
- setuptools
run:
- python
about:
home: http://pythonhosted.org/PyDAQmx/
license: BSD
summary: PyDAQmx allows users to use data acquisition hardware from National Instruments with Python. It provides an interface between the NIDAQmx driver and Python. The package works on Windows and Linux.'

View File

@ -1 +0,0 @@
%PYTHON% setup.py install

View File

@ -1 +0,0 @@
$PYTHON setup.py install

View File

@ -1,26 +0,0 @@
package:
name: pyelftools
version: 0.23
source:
git_url: https://github.com/eliben/pyelftools.git
git_tag: v0.23
build:
number: 0
requirements:
build:
- python
- setuptools
run:
- python
test:
imports:
- elftools
about:
home: https://github.com/eliben/pyelftools.git
license: Public domain
summary: 'Library for analyzing ELF files and DWARF debugging information'

View File

@ -1,3 +0,0 @@
set LIBGIT2=%PREFIX%
set VS100COMNTOOLS=%VS120COMNTOOLS%
%PYTHON% setup.py install

View File

@ -1,2 +0,0 @@
export LIBGIT2=$PREFIX
$PYTHON setup.py install

View File

@ -1,28 +0,0 @@
package:
name: pygit2
version: 0.22.1
source:
git_url: https://github.com/libgit2/pygit2
git_tag: v0.22.1
build:
number: 1
requirements:
build:
- system # [linux]
- python
- libgit2
- cffi >=0.8.1
- pkgconfig # [linux]
run:
- system # [linux]
- python
- libgit2
- cffi >=0.8.1
about:
home: http://www.pygit2.org/
license: GPLv2 with a special Linking Exception
summary: 'Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 implements the core of Git.'

View File

@ -1,2 +0,0 @@
"%PYTHON%" setup.py install
if errorlevel 1 exit 1

View File

@ -1,3 +0,0 @@
#!/bin/bash
$PYTHON setup.py install

View File

@ -1,27 +0,0 @@
package:
name: pyqtgraph
version: 0.9.10.1036edf
source:
git_url: https://github.com/pyqtgraph/pyqtgraph.git
git_rev: 1036edf
requirements:
build:
- python
- setuptools
- numpy
run:
- python
- numpy
- pyqt >=4.7
test:
imports:
- pyqtgraph
about:
home: http://www.pyqtgraph.org
license: MIT License
summary: 'Scientific Graphics and GUI Library for Python'

View File

@ -1,2 +0,0 @@
pip install regex
%PYTHON% setup.py install

View File

@ -1,2 +0,0 @@
pip install regex
$PYTHON setup.py install

View File

@ -1,24 +0,0 @@
package:
name: pythonparser
version: 0.0
source:
git_url: https://github.com/m-labs/pythonparser
git_tag: master
build:
number: 0
requirements:
build:
- python
- setuptools
test:
imports:
- pythonparser
about:
home: http://m-labs.hk/pythonparser/
license: BSD
summary: 'PythonParser is a Python parser written specifically for use in tooling. It parses source code into an AST that is a superset of Pythons built-in ast module, but returns precise location information for every token.'

View File

@ -1 +0,0 @@
%PYTHON% setup.py install

View File

@ -1,3 +0,0 @@
#!/bin/bash
$PYTHON setup.py install

Some files were not shown because too many files have changed in this diff Show More