コード例 #1
0
    def setup_customized_image(self):
        logger.debug('Checking dependencies and container setup')

        self.check_docker()

        commands = []
        env_vars = self.config.image_setup.get('env', {})

        commands += self.get_ppa_adding_commands()

        dependencies = self.get_dependency_packages()
        if dependencies:
            commands.append(
                'echo set debconf/frontend Noninteractive | debconf-communicate && echo set debconf/priority critical | debconf-communicate'
            )
            commands.append('apt-get update && {} && apt-get clean'.format(
                self.get_apt_install_cmd(dependencies)))

        if self.config.image_setup:
            commands.extend(self.config.image_setup.get('run', []))

        dockerfile_content = self.construct_dockerfile_content(
            commands, env_vars)

        if self.is_dockerfile_outdated(dockerfile_content):
            self.create_custom_container(dockerfile_content)
        else:
            logger.debug('Image already setup')
コード例 #2
0
    def setup_container_mode(self):
        ppa_commands = self.get_ppa_adding_commands()
        if ppa_commands:
            self.run_command(' && '.join(ppa_commands))

        dependencies = self.get_dependency_packages()
        if dependencies:
            self.run_command('apt-get update', use_build_dir=False)

            run = False
            for dep in dependencies:
                exists = ''
                try:
                    exists = self.run_command(
                        'dpkg -s {} | grep Status'.format(dep),
                        get_output=True,
                        use_build_dir=False)
                except subprocess.CalledProcessError:
                    exists = ''

                if exists.strip() != 'Status: install ok installed':
                    run = True
                    break

            if run:
                self.run_command(self.get_apt_install_cmd(dependencies),
                                 use_build_dir=False)
            else:
                logger.debug('Dependencies already installed')

        if self.config.image_setup:
            for command in self.config.image_setup.get('run', []):
                self.run_command(command, use_build_dir=False)
コード例 #3
0
    def kill_gdbserver(self):
        processes = self.device.run_command("ps aux | grep gdbserver",
                get_output=True)

        for line in processes.splitlines():
            if "gdbserver localhost:{}".format(self.port) in line:
                logger.debug("Killing running gdbserver on device")
                pid = line.split()[1]
                self.device.run_command("kill -9 {}".format(pid))
コード例 #4
0
    def run_app(self):
        docker_config = self.setup_docker()

        if self.ide_delegate is not None:
            self.ide_delegate.before_run(self.config, docker_config)

        command = docker_config.render_command()
        logger.debug(command)

        subprocess.check_call(shlex.split(command), cwd=docker_config.working_directory)
コード例 #5
0
ファイル: __init__.py プロジェクト: loganfutch2/clickable
    def run(self, commands=[], args=None):
        self.config = Config(
            args=args,
            clickable_version=__version__,
            desktop=('desktop' in commands or 'test' in commands),
        )
        self.config.container = Container(self.config)

        VALID_COMMANDS = self.command_names + list(self.config.scripts.keys())

        if len(commands) == 0:
            commands = self.config.default.split(' ')
        '''
        Detect senarios when an argument is passed to a command. For example:
        `clickable install /path/to/click`. Since clickable allows commands
        to be strung together it makes detecting this harder. This check has
        been limited to just the case when we have 2 values in args.commands as
        stringing together multiple commands and a command with an argument is
        unlikely to occur.
        TODO determine if there is a better way to do this.
        '''
        command_arg = ''
        if len(commands) == 2 and commands[1] not in VALID_COMMANDS:
            command_arg = commands[1]
            commands = commands[:1]

        commands = [
            self.command_aliases[command]
            if command in self.command_aliases else command
            for command in commands
        ]

        for command in commands:
            if command in self.command_names:
                cmd = self.command_classes[command](self.config)
                cmd.preprocess(command_arg)

        # TODO consider removing the ability to string together multiple commands
        # This should help clean up the arguments & command_arg
        for command in commands:
            if command in self.config.scripts:
                logger.debug('Running the "{}" script'.format(command))
                subprocess.check_call(self.config.scripts[command],
                                      cwd=self.config.cwd,
                                      shell=True)
            elif command in self.command_names:
                logger.debug('Running the "{}" command'.format(command))
                cmd = self.command_classes[command](self.config)
                cmd.run(command_arg)
            else:
                logger.error(
                    'There is no builtin or custom command named "{}"'.format(
                        command))
                self.print_valid_commands()
                sys.exit(1)
コード例 #6
0
ファイル: ide.py プロジェクト: bhdouglass/clickable
    def run(self, path_arg=None):
        if not path_arg:
            raise ClickableException('No command supplied for `clickable ide`')

        #get the preprocessor according to command if any
        if 'qtcreator' in path_arg.split():
            self.ide_delegate = QtCreatorDelegate()
            path_arg = self.ide_delegate.override_command(path_arg)
            logger.debug(
                'QtCreator command detected. Changing command to: {}'.format(
                    path_arg))

        self.command = path_arg
        super().run()
コード例 #7
0
    def get_time_zone(self):
        try:
            return run_subprocess_check_output(
                'timedatectl show -p Timezone --value',
                stderr=subprocess.DEVNULL)
        except:
            logger.debug(
                'timedatectl show command failed. Falling back to alternative way to detect timezone...'
            )

        if os.path.exists('/etc/timezone'):
            with open('/etc/timezone') as host_timezone_file:
                return host_timezone_file.readline().strip()
        else:
            logger.debug(
                '/etc/timezone does not exist. Falling back to alternative way to detect timezone...'
            )

        try:
            output = run_subprocess_check_output('timedatectl status')
            for line in output.splitlines():
                line = line.strip()
                if line.startswith('Time zone:'):
                    start = line.find(':') + 1
                    end = line.find('(')
                    return line[start:end].strip()
        except:
            logger.debug(
                "timedatctl status method failed to set timezone from host in desktop mode..."
            )

        logger.debug("Falling back to UTC as timezone.")
        return 'UTC'
コード例 #8
0
    def run(self, path_arg=None):
        if self.config.is_desktop_mode():
            logger.debug('Skipping logs, running in desktop mode')
            return
        elif self.config.container_mode:
            logger.debug('Skipping logs, running in container mode')
            return

        log = '~/.cache/upstart/application-click-{}.log'.format(
            self.config.install_files.find_full_package_name(), )

        if self.config.log:
            log = self.config.log

        self.device.run_command('tail -f {}'.format(log))
コード例 #9
0
ファイル: log.py プロジェクト: loganfutch2/clickable
    def run(self, path_arg=None):
        if self.config.desktop:
            logger.debug('Skipping log, running in desktop mode')
            return
        elif self.config.container_mode:
            logger.debug('Skipping log, running in container mode')
            return

        log = '~/.cache/upstart/application-click-{}_{}_{}.log'.format(
            self.config.find_package_name(), self.config.find_app_name(),
            self.config.find_version())

        if self.config.log:
            log = self.config.log

        self.device.run_command('cat {}'.format(log))
コード例 #10
0
ファイル: launch.py プロジェクト: bhdouglass/clickable
    def kill(self):
        if self.config.is_desktop_mode():
            logger.debug('Skipping kill, running in desktop mode')
            return
        elif self.config.container_mode:
            logger.debug('Skipping kill, running in container mode')
            return

        if self.config.kill:
            try:
                # Enclose first character in square brackets to prevent
                # spurious error when running `pkill -f` over `adb`
                kill = '[' + self.config.kill[:1] + ']' + self.config.kill[1:]
                self.device.run_command('pkill -f \\"{}\\"'.format(kill))
            except Exception:
                pass  # Nothing to do, the process probably wasn't running
コード例 #11
0
    def click_build(self):
        command = 'click build {} --no-validate'.format(
            self.config.install_dir)

        self.config.container.run_command(command)

        if self.config.click_output:
            click = self.config.get_click_filename()
            click_path = os.path.join(self.config.build_dir, click)
            output_file = os.path.join(self.config.click_output, click)

            if not os.path.exists(self.config.click_output):
                os.makedirs(self.config.click_output)

            logger.debug('Click outputted to {}'.format(output_file))
            shutil.copyfile(click_path, output_file)
コード例 #12
0
    def run(self, path_arg=None):
        if self.config.is_desktop_mode():
            logger.debug('Skipping install, running in desktop mode')
            return
        elif self.config.container_mode:
            logger.debug('Skipping install, running in container mode')
            return

        cwd = '.'
        if path_arg:
            click = os.path.basename(path_arg)
            click_path = path_arg
        else:
            click = self.config.install_files.get_click_filename()
            click_path = os.path.join(self.config.build_dir, click)
            cwd = self.config.build_dir

        if self.config.ssh:
            command = 'scp {} phablet@{}:/home/phablet/'.format(
                click_path, self.config.ssh)
            run_subprocess_check_call(command, cwd=cwd, shell=True)

        else:
            self.device.check_any_attached()

            if self.config.device_serial_number:
                command = 'adb -s {} push {} /home/phablet/'.format(
                    self.config.device_serial_number, click_path)
            else:
                self.device.check_multiple_attached()
                command = 'adb push {} /home/phablet/'.format(click_path)

            run_subprocess_check_call(command, cwd=cwd, shell=True)

        if path_arg:
            logger.info(
                "Skipping uninstall step, because you specified a click package."
            )
        else:
            self.try_uninstall()

        self.device.run_command(
            'pkcon install-local --allow-untrusted /home/phablet/{}'.format(
                click),
            cwd=cwd)
        self.device.run_command('rm /home/phablet/{}'.format(click), cwd=cwd)
コード例 #13
0
    def __init__(self, name, json_config, arch, root_dir, debug_build):
        self.debug_build = debug_build

        self.config = {
            'name': name,
            'arch': arch,
            'arch_triplet': None,
            'template': None,
            'postmake': None,
            'prebuild': None,
            'build': None,
            'postbuild': None,
            'build_dir': '$ROOT/build/$ARCH_TRIPLET/$NAME',
            'src_dir': '$ROOT/libs/$NAME',
            'root_dir': root_dir,
            'dependencies_build': [],
            'dependencies_target': [],
            'dependencies_ppa': [],
            'make_jobs': 0,
            'docker_image': None,
            'build_args': [],
            'make_args': [],
            'install_dir': '$BUILD_DIR/install'
        }

        self.config.update(json_config)

        self.cleanup_config()

        self.config['arch_triplet'] = self.arch_triplet_mapping[
            self.config['arch']]

        for key in self.path_keys:
            if key not in self.accepts_placeholders and self.config[key]:
                self.config[key] = os.path.abspath(self.config[key])

        self.substitute_placeholders()
        self.set_env_vars()

        self.check_config_errors()

        for key, value in self.config.items():
            logger.debug('Lib {} config value {}: {}'.format(name, key, value))
コード例 #14
0
    def restore_cached_container(self):
        with open(self.docker_name_file, 'r') as f:
            cached_container = f.read().strip()

            if not image_exists(cached_container):
                logger.warning("Cached container does not exist anymore")
                return

            command_base = 'docker images -q {}'.format(self.base_docker_image)
            command_cached = 'docker history -q {}'.format(cached_container)

            hash_base = run_subprocess_check_output(command_base).strip()
            history_cached = run_subprocess_check_output(
                command_cached).strip()

            if hash_base in history_cached:
                logger.debug("Found cached container")
                self.docker_image = cached_container
            else:
                logger.warning("Found outdated container")
コード例 #15
0
    def restore_cached_image(self):
        if not os.path.exists(self.docker_name_file):
            return

        with open(self.docker_name_file, 'r') as f:
            cached_image = None
            cached_base_image = None

            try:
                image_file = json.load(f)
                cached_image = image_file.get('name', None)
                cached_base_image = image_file.get('base_image', None)
            except ValueError:
                pass

            if not cached_image:
                logger.warning("Cached image file is invalid")
                return

            if not image_exists(cached_image):
                logger.warning("Cached container does not exist anymore")
                return

            if self.base_docker_image != cached_base_image:
                logger.warning("Cached image has a different base image")

            self.check_docker()

            command_base = 'docker images -q {}'.format(self.base_docker_image)
            command_cached = 'docker history -q {}'.format(cached_image)

            hash_base = run_subprocess_check_output(command_base).strip()
            history_cached = run_subprocess_check_output(
                command_cached).strip()

            if hash_base in history_cached:
                logger.debug("Found cached container")
                self.docker_image = cached_image
            else:
                logger.warning("Cached container is outdated")
コード例 #16
0
def main():
    clickable = Clickable()
    args = clickable.parse_args()

    if args.verbose:
        console_handler.setLevel(logging.DEBUG)
    logger.debug('Clickable v' + __version__)

    try:
        clickable.run(args.commands, args)
    except ClickableException as e:
        logger.error(str(e))
        sys.exit(1)
    except subprocess.CalledProcessError as e:
        logger.debug('Command exited with an error:' + str(e.cmd), exc_info=e)
        logger.critical(
            'Command exited with non-zero exit status {}, see above for details. This is most likely not a problem with Clickable.'
            .format(e.returncode, ))

        sys.exit(2)
    except Exception as e:
        logger.debug('Encountered an unknown error', exc_info=e)
        if not args.verbose:
            logger.critical('Encountered an unknown error: ' + str(e))

        logger.critical(
            'If you believe this is a bug, please file a report at https://gitlab.com/clickable/clickable/issues with the log file located at '
            + log_file)
        sys.exit(3)
コード例 #17
0
    def check_base_image_version(self):
        if not self.minimum_version:
            return

        if not image_exists(self.docker_image):
            return

        version = 0
        try:
            format_string = '{{ index .Config.Labels "image_version"}}'
            command = "docker inspect --format '{}' {}".format(
                format_string, self.docker_image)
            logger.debug(
                'Checking docker image version via: {}'.format(command))

            version_string = run_subprocess_check_output(command)
            version = int(version_string)
        except (ValueError, subprocess.CalledProcessError):
            logger.warn("Could not read the image version from the container")

        if version < self.minimum_version:
            raise ClickableException(
                'This version of Clickable requires Clickable docker image {} in version {} or higher (found version {}). Please run "clickable update" to update your local images.'
                .format(self.docker_image, self.minimum_version, version))
コード例 #18
0
    def create_custom_container(self, dockerfile_content):
        if not os.path.exists(self.clickable_dir):
            os.makedirs(self.clickable_dir)

        with open(self.docker_file, 'w') as f:
            f.write(dockerfile_content)

        self.docker_image = '{}-{}'.format(self.base_docker_image,
                                           uuid.uuid4())
        with open(self.docker_name_file, 'w') as f:
            json.dump(
                {
                    'name': self.docker_image,
                    'base_image': self.base_docker_image,
                }, f)

        logger.debug('Generating new docker image')
        try:
            subprocess.check_call(shlex.split('docker build -t {} .'.format(
                self.docker_image)),
                                  cwd=self.clickable_dir)
        except subprocess.CalledProcessError:
            self.clean_clickable()
            raise
コード例 #19
0
def main():
    clickable = Clickable()
    args = clickable.parse_args()

    if args.verbose:
        console_handler.setLevel(logging.DEBUG)
    logger.debug('Clickable v' + __version__)
    clickable.check_version(quiet=True)

    try:
        clickable.run(args.commands, args)
    except ClickableException as e:
        logger.error(str(e))
        sys.exit(1)
    except subprocess.CalledProcessError as e:
        logger.debug('Command exited with an error:' + str(e.cmd), exc_info=e)
        logger.critical(
            'Command exited with non-zero exit status {}, see above for details. This is most likely not a problem with Clickable.'
            .format(e.returncode, ))

        sys.exit(2)
    except KeyboardInterrupt as e:
        logger.info(
            '')  # Print an empty space at then end so the cli prompt is nicer
        sys.exit(0)
    except Exception as e:
        if isinstance(e, OSError) and '28' in str(e):
            logger.critical('No space left on device')
            sys.exit(2)
            return

        logger.debug('Encountered an unknown error', exc_info=e)
        if not args.verbose:
            logger.critical('Encountered an unknown error: ' + str(e))

        logger.critical(
            'If you believe this is a bug, please file a report at https://gitlab.com/clickable/clickable/issues with the log file located at '
            + log_file)
        sys.exit(3)
コード例 #20
0
ファイル: libconfig.py プロジェクト: bhdouglass/clickable
    def __init__(self, name, json_config, arch, root_dir, qt_version,
                 debug_build, verbose):
        # Must come after ARCH_TRIPLET to avoid breaking it
        self.placeholders.update({"ARCH": "arch"})

        self.qt_version = qt_version
        self.debug_build = debug_build
        self.verbose = verbose

        self.set_host_arch()
        self.container_list = list(
            Constants.container_mapping[self.host_arch].values())

        self.config = {
            'name': name,
            'arch': arch,
            'arch_triplet': None,
            'template': None,
            'builder': None,
            'postmake': None,
            'prebuild': None,
            'build': None,
            'postbuild': None,
            'build_dir': '${ROOT}/build/${ARCH_TRIPLET}/${NAME}',
            'build_home': '${BUILD_DIR}/.clickable/home',
            'src_dir': '${ROOT}/libs/${NAME}',
            'root_dir': root_dir,
            'dependencies_build': [],
            'dependencies_host': [],
            'dependencies_target': [],
            'dependencies_ppa': [],
            'make_jobs': None,
            'docker_image': None,
            'build_args': [],
            'env_vars': {},
            'make_args': [],
            'install_dir': '${BUILD_DIR}/install',
            'image_setup': {},
            'test': 'ctest',
        }

        # TODO remove support for deprecated "template" in clickable.json
        if "template" in json_config:
            logger.warning(
                'Parameter "template" is deprecated in clickable.json. Use "builder" as drop-in replacement instead.'
            )
            json_config["builder"] = json_config["template"]
            json_config["template"] = None

        self.config.update(json_config)
        if self.config["docker_image"]:
            self.is_custom_docker_image = True
        else:
            self.is_custom_docker_image = False

        self.cleanup_config()

        self.config['arch_triplet'] = Constants.arch_triplet_mapping[
            self.config['arch']]

        for key in self.path_keys:
            if key not in self.accepts_placeholders and self.config[key]:
                self.config[key] = os.path.abspath(self.config[key])

        self.substitute_placeholders()
        self.set_env_vars()

        self.check_config_errors()

        for key, value in self.config.items():
            logger.debug('Lib {} config value {}: {}'.format(name, key, value))
コード例 #21
0
    def setup_dependencies(self, force_build=False):
        if self.config.dependencies_build or self.config.dependencies_target:
            logger.debug('Checking dependencies')

            dependencies = self.config.dependencies_build
            for dep in self.config.dependencies_target:
                if ':' in dep:
                    dependencies.append(dep)
                else:
                    dependencies.append('{}:{}'.format(dep, self.config.arch))

            if self.config.container_mode:
                self.run_command('apt-get update', sudo=True, use_dir=False)

                command = 'apt-get install -y --force-yes'
                run = False
                for dep in dependencies:
                    exists = ''
                    try:
                        exists = self.run_command(
                            'dpkg -s {} | grep Status'.format(dep),
                            get_output=True,
                            use_dir=False)
                    except subprocess.CalledProcessError:
                        exists = ''

                    if exists.strip() != 'Status: install ok installed':
                        run = True
                        command = '{} {}'.format(command, dep)

                if run:
                    self.run_command(command, sudo=True, use_dir=False)
                else:
                    logger.debug('Dependencies already installed')
            else:
                self.check_docker()

                if self.config.custom_docker_image:
                    logger.warning(
                        'Skipping dependency check, using a custom docker image'
                    )
                else:
                    command_ppa = ''
                    if self.config.dependencies_ppa:
                        command_ppa = 'RUN add-apt-repository {}'.format(
                            ' '.join(self.config.dependencies_ppa))
                    dockerfile = '''
FROM {}
RUN echo set debconf/frontend Noninteractive | debconf-communicate && echo set debconf/priority critical | debconf-communicate
{}
RUN apt-get update && apt-get install -y --force-yes --no-install-recommends {} && apt-get clean
                    '''.format(self.base_docker_image, command_ppa,
                               ' '.join(dependencies)).strip()

                    build = force_build

                    if not os.path.exists(self.clickable_dir):
                        os.makedirs(self.clickable_dir)

                    if self.docker_image != self.base_docker_image and os.path.exists(
                            self.docker_file):
                        with open(self.docker_file, 'r') as f:
                            if dockerfile.strip() != f.read().strip():
                                build = True
                    else:
                        build = True

                    if not build:
                        command = 'docker images -q {}'.format(
                            self.docker_image)
                        image_exists = run_subprocess_check_output(
                            command).strip()
                        build = not image_exists

                    if build:
                        with open(self.docker_file, 'w') as f:
                            f.write(dockerfile)

                        self.docker_image = '{}-{}'.format(
                            self.base_docker_image, uuid.uuid4())
                        with open(self.docker_name_file, 'w') as f:
                            f.write(self.docker_image)

                        logger.debug('Generating new docker image')
                        try:
                            subprocess.check_call(shlex.split(
                                'docker build -t {} .'.format(
                                    self.docker_image)),
                                                  cwd=self.clickable_dir)
                        except subprocess.CalledProcessError:
                            self.clean_clickable()
                            raise
                    else:
                        logger.debug('Dependencies already setup')
コード例 #22
0
    def check_version(self, quiet=False):
        if requests_available:
            version = None
            check = True
            version_check = expanduser('~/.clickable/version_check.json')
            if isfile(version_check):
                with open(version_check, 'r') as f:
                    try:
                        version_check_data = json.load(f)
                    except ValueError:
                        version_check_data = None

                if version_check_data and 'version' in version_check_data and 'datetime' in version_check_data:
                    last_check = datetime.strptime(
                        version_check_data['datetime'], DATE_FORMAT)
                    if last_check > (datetime.now() - timedelta(days=2)) and \
                        'current_version' in version_check_data and \
                        version_check_data['current_version'] == __version__:

                        check = False
                        version = version_check_data['version']
                        logger.debug('Using cached version check')

            if check:
                logger.debug('Checking for updates to clickable')

                try:
                    response = requests.get(
                        'https://clickable-ut.dev/en/latest/_static/version.json',
                        timeout=5)
                    response.raise_for_status()

                    data = response.json()
                    version = data['version']
                except requests.exceptions.Timeout as e:
                    logger.warning(
                        'Unable to check for updates to clickable, the request timedout'
                    )
                except Exception as e:
                    logger.debug('Version check failed:' + str(e.cmd),
                                 exc_info=e)
                    logger.warning(
                        'Unable to check for updates to clickable, an unknown error occurred'
                    )

                if version:
                    with open(version_check, 'w') as f:
                        json.dump(
                            {
                                'version': version,
                                'datetime':
                                datetime.now().strftime(DATE_FORMAT),
                                'current_version': __version__,
                            }, f)

            if version:
                if version != __version__:
                    logger.info(
                        'v{} of clickable is available, update to get the latest features and improvements!'
                        .format(version))
                else:
                    if not quiet:
                        logger.info(
                            'You are running the latest version of clickable!')
        else:
            if not quiet:
                logger.warning(
                    'Unable to check for updates to clickable, please install "requests"'
                )
コード例 #23
0
    def run_command(self,
                    command,
                    sudo=False,
                    get_output=False,
                    use_dir=True,
                    cwd=None):
        wrapped_command = command
        cwd = cwd if cwd else os.path.abspath(self.config.root_dir)

        if self.config.container_mode:
            wrapped_command = 'bash -c "{}"'.format(command)
        else:  # Docker
            self.check_docker()

            if ' ' in cwd or ' ' in self.config.build_dir:
                raise ClickableException(
                    'There are spaces in the current path, this will cause errors in the build process'
                )

            if self.config.first_docker_info:
                logger.debug('Using docker container "{}"'.format(
                    self.docker_image))
                self.config.first_docker_info = False

            go_config = ''
            if self.config.gopath:
                gopaths = self.config.gopath.split(':')

                docker_gopaths = []
                go_configs = []
                for (index, path) in enumerate(gopaths):
                    go_configs.append('-v {}:/gopath/path{}:Z'.format(
                        path, index))
                    docker_gopaths.append('/gopath/path{}'.format(index))

                go_config = '{} -e GOPATH={}'.format(
                    ' '.join(go_configs),
                    ':'.join(docker_gopaths),
                )

            rust_config = ''

            if self.config.config[
                    'template'] == Config.RUST and self.config.cargo_home:
                logger.info("Caching cargo related files in {}".format(
                    self.config.cargo_home))
                cargo_registry = os.path.join(self.config.cargo_home,
                                              'registry')
                cargo_git = os.path.join(self.config.cargo_home, 'git')
                cargo_package_cache_lock = os.path.join(
                    self.config.cargo_home, '.package-cache')

                os.makedirs(cargo_registry, exist_ok=True)
                os.makedirs(cargo_git, exist_ok=True)

                # create .package-cache if it doesn't exist
                with open(cargo_package_cache_lock, "a"):
                    pass

                rust_config = '-v {}:/opt/rust/cargo/registry:Z -v {}:/opt/rust/cargo/git:Z -v {}:/opt/rust/cargo/.package-cache'.format(
                    cargo_registry,
                    cargo_git,
                    cargo_package_cache_lock,
                )

            env_vars = self.config.prepare_docker_env_vars()

            wrapped_command = 'docker run -v {}:{}:Z {} {} {} -w {} -u {} -e HOME=/tmp --rm -i {} bash -c "{}"'.format(
                cwd,
                cwd,
                env_vars,
                go_config,
                rust_config,
                self.config.build_dir if use_dir else cwd,
                os.getuid(),
                self.docker_image,
                command,
            )

        kwargs = {}
        if use_dir:
            kwargs['cwd'] = self.config.build_dir

        if get_output:
            return run_subprocess_check_output(shlex.split(wrapped_command),
                                               **kwargs)
        else:
            subprocess.check_call(shlex.split(wrapped_command), **kwargs)
コード例 #24
0
    def run_docker_command(self, docker_config):
        command = docker_config.render_command()
        logger.debug(command)

        subprocess.check_call(shlex.split(command), cwd=docker_config.working_directory)
コード例 #25
0
    def run_command(self,
                    command,
                    root_user=False,
                    get_output=False,
                    use_build_dir=True,
                    cwd=None,
                    tty=False,
                    localhost=False):
        wrapped_command = command
        cwd = cwd if cwd else os.path.abspath(self.config.root_dir)

        if self.config.container_mode:
            wrapped_command = 'bash -c "{}"'.format(command)
        else:  # Docker
            self.check_docker()

            if ' ' in cwd or ' ' in self.config.build_dir:
                raise ClickableException(
                    'There are spaces in the current path, this will cause errors in the build process'
                )

            if self.config.first_docker_info:
                logger.debug('Using docker container "{}"'.format(
                    self.docker_image))
                self.config.first_docker_info = False

            go_config = ''
            if self.config.builder == Constants.GO and self.config.gopath:
                gopaths = self.config.gopath.split(':')
                docker_gopaths = [
                    '/gopath/path{}'.format(index)
                    for index in range(len(gopaths))
                ]
                go_config = '-e GOPATH={}'.format(':'.join(docker_gopaths), )

            rust_config = ''

            if self.config.builder == Constants.RUST and self.config.cargo_home:
                logger.info("Caching cargo related files in {}".format(
                    self.config.cargo_home))

            env_vars = self.config.prepare_docker_env_vars()

            user = ""
            if not root_user:
                user = "******".format(os.getuid())

            mounts = self.render_mounts(
                self.get_docker_mounts(transparent=[cwd]))

            wrapped_command = 'docker run {mounts} {env} {go} {rust} {user} -w {cwd} --rm {tty} {network} -i {image} bash -c "{cmd}"'.format(
                mounts=mounts,
                env=env_vars,
                go=go_config,
                rust=rust_config,
                cwd=self.config.build_dir if use_build_dir else cwd,
                user=user,
                image=self.docker_image,
                cmd=command,
                tty="-t" if tty else "",
                network='--network="host"' if localhost else "",
            )

        kwargs = {}
        if use_build_dir:
            kwargs['cwd'] = self.config.build_dir

        if get_output:
            return run_subprocess_check_output(shlex.split(wrapped_command),
                                               **kwargs)
        else:
            subprocess.check_call(shlex.split(wrapped_command), **kwargs)
コード例 #26
0
ファイル: qtcreator.py プロジェクト: bhdouglass/clickable
    def init_cmake_project(self, config, docker_config):

        executable = config.project_files.find_any_executable()
        exec_args = " ".join(config.project_files.find_any_exec_args())

        #don't do all that if exec line not found
        if not executable:
            return

        choice = input(
            Colors.INFO +
            'Do you want Clickable to setup a QtCreator project for you? [Y/n]: '
            + Colors.CLEAR).strip().lower()
        if choice != 'y' and choice != 'yes' and choice != '':
            return

        #CLICK_EXE can be a variable
        match_exe_var = re.match("@([-\w]+)@", executable)
        if match_exe_var:
            #catch the variable name and try to get it from CMakeLists.txt
            cmd_var = match_exe_var.group(1)
            final_cmd = self.cmake_guess_exec_command(cmd_var)
            if final_cmd is not None:
                try:
                    exe, exe_arg = final_cmd.split(' ', maxsplit=1)
                except:
                    exe, exe_arg = final_cmd, ''

                executable = exe
                exec_args = exe_arg
                logger.debug(
                    'found that executable is {} with args: {}'.format(
                        exe, exe_arg))
            else:
                #was not able to guess executable
                logger.warning(
                    "Could not determine executable command '{}', please adjust your project's run settings"
                    .format(executable))

        # work around for qtcreator bug when first run of a project to avoid qtcreator hang
        # we need to create the build directory first
        if not os.path.isdir(config.build_dir):
            os.makedirs(config.build_dir)

        env_vars = docker_config.environment
        clickable_env_path = '{}:{}'.format(env_vars["PATH"],
                                            env_vars["CLICK_PATH"])
        clickable_ld_library_path = '{}:{}'.format(
            env_vars["LD_LIBRARY_PATH"], env_vars["CLICK_LD_LIBRARY_PATH"])
        clickable_qml2_import_path = '{}:{}:{}'.format(
            env_vars["QML2_IMPORT_PATH"], env_vars["CLICK_QML2_IMPORT_PATH"],
            os.path.join(config.install_dir, 'lib'))

        template_replacement = {
            "CLICKABLE_LD_LIBRARY_PATH": clickable_ld_library_path,
            "CLICKABLE_QML2_IMPORT_PATH": clickable_qml2_import_path,
            "CLICKABLE_BUILD_DIR": config.build_dir,
            "CLICKABLE_INSTALL_DIR": config.install_dir,
            "CLICKABLE_EXEC_CMD": executable,
            "CLICKABLE_EXEC_ARGS": exec_args,
            "CLICKABLE_SRC_DIR": config.src_dir,
            "CLICKABLE_BUILD_ARGS": " ".join(config.build_args),
            "CLICKABLE_PATH": clickable_env_path
        }

        output_path = os.path.join(self.project_path,
                                   'CMakeLists.txt.user.shared')
        #now read template and generate the .shared file to the root project dir
        with open(self.template_path, "r") as infile2, open(output_path,
                                                            "w") as outfile:
            for line in infile2:
                for f_key, f_value in template_replacement.items():
                    if f_key in line:
                        line = line.replace(f_key, f_value)
                outfile.write(line)

        logger.info(
            'generated default build/run template to {}'.format(output_path))
コード例 #27
0
    def run(self, arg_commands=[], args=None):
        self.config = self.setup_config(args, arg_commands)
        self.config.container = Container(
            self.config, minimum_version=__container_minimum_required__)
        commands = self.config.commands

        VALID_COMMANDS = self.command_names + list(self.config.scripts.keys())

        is_default = not arg_commands
        '''
        Detect senarios when an argument is passed to a command. For example:
        `clickable install /path/to/click`. Since clickable allows commands
        to be strung together it makes detecting this harder. This check has
        been limited to just the case when we have 2 values in args.commands as
        stringing together multiple commands and a command with an argument is
        unlikely to occur.
        TODO remove chaining and clean this up
        '''
        command_arg = ''
        if len(commands) == 2 and commands[1] not in VALID_COMMANDS:
            command_arg = commands[1]
            commands = commands[:1]

        commands = [
            self.command_aliases[command]
            if command in self.command_aliases else command
            for command in commands
        ]
        if len(commands) > 1 and not is_default:
            logger.warning(
                'Chaining multiple commands is deprecated and will be rejected in a future version of Clickable.'
            )

        for command in commands:
            if command in self.command_names:
                cmd = self.command_classes[command](self.config)
                cmd.preprocess(command_arg)

        for command in commands:
            if command == 'bash-completion':
                cli_args = [
                    '--serial-number',
                    '--config',
                    '--ssh',
                    '--arch',
                    '--verbose',
                    '--container-mode',
                    '--apikey',
                    '--docker-image',
                    '--dirty',
                    '--debug',
                ]
                print(' '.join(sorted(VALID_COMMANDS + cli_args)))
            elif command == 'bash-completion-desktop':
                cli_args = [
                    '--nvidia',
                    '--no-nvidia'
                    '--gdbserver',
                    '--gdb',
                    '--dark-mode',
                    '--lang',
                    '--skip-build',
                    '--dirty',
                    '--verbose',
                    '--config',
                ]
                print(' '.join(sorted(cli_args)))
            elif command in self.config.scripts:
                logger.debug('Running the "{}" script'.format(command))
                subprocess.check_call(self.config.scripts[command],
                                      cwd=self.config.cwd,
                                      shell=True)
            elif command in self.command_names:
                logger.debug('Running the "{}" command'.format(command))
                cmd = self.command_classes[command](self.config)
                cmd.run(command_arg)
            else:
                logger.error(
                    'There is no builtin or custom command named "{}"'.format(
                        command))
                self.print_valid_commands()
                sys.exit(1)