From 177c26c3ea4e437b99dd15bba9263db12392366f Mon Sep 17 00:00:00 2001 From: Chandan Singh Date: Fri, 11 May 2018 16:59:21 +0100 Subject: _frontend/cli.py: Allow specifying elements by absolute/relative path --- buildstream/_frontend/cli.py | 72 ++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py index 3c91cf80d..e22d7f674 100644 --- a/buildstream/_frontend/cli.py +++ b/buildstream/_frontend/cli.py @@ -68,7 +68,7 @@ def override_completions(cmd_param, args, incomplete): # We can't easily extend click's data structures without # modifying click itself, so just do some weak special casing # right here and select which parameters we want to handle specially. - if isinstance(cmd_param.type, click.Path) and \ + if isinstance(cmd_param.type, ElementParamType) and \ (cmd_param.name == 'elements' or cmd_param.name == 'element' or cmd_param.name == 'except_' or @@ -108,6 +108,40 @@ original_main = click.BaseCommand.main click.BaseCommand.main = override_main +################################################################## +# Custom Paramaeter Types # +################################################################## + +class ElementParamType(click.ParamType): + name = 'Element' + + def convert(self, value, param, ctx): + target = value + + # If we are dealing with relative/absolute path to element and + # not target name, normalize the path into target name. + if value.startswith('.') or os.path.isabs(value): + fullpath = os.path.realpath(value) + base_directory = os.path.abspath(ctx.parent.params['directory']) + project_file = os.path.join(base_directory, 'project.conf') + project = _yaml.load(project_file) + element_directory = project.get('element-path') + if element_directory: + base_directory = os.path.join(base_directory, element_directory) + + # Ensure that element is not outside elements directory. + # This is kind of ugly but python 3.4 does not has + # os.path.commonpath() + if not fullpath.startswith(base_directory + os.path.sep): + raise click.BadParameter('{} is outside project context'.format(fullpath), + ctx=ctx, + param=param) + + target = os.path.relpath(fullpath, base_directory) + + return target + + ################################################################## # Main Options # ################################################################## @@ -206,20 +240,20 @@ def init(app, project_name, format_version, element_path, force): @click.option('--all', 'all_', default=False, is_flag=True, help="Build elements that would not be needed for the current build plan") @click.option('--track', 'track_', multiple=True, - type=click.Path(dir_okay=False, readable=True), + type=ElementParamType(), help="Specify elements to track during the build. Can be used " "repeatedly to specify multiple elements") @click.option('--track-all', default=False, is_flag=True, help="Track all elements in the pipeline") @click.option('--track-except', multiple=True, - type=click.Path(dir_okay=False, readable=True), + type=ElementParamType(), help="Except certain dependencies from tracking") @click.option('--track-cross-junctions', '-J', default=False, is_flag=True, help="Allow tracking to cross junction boundaries") @click.option('--track-save', default=False, is_flag=True, help="Deprecated: This is ignored") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def build(app, elements, all_, track_, track_save, track_all, track_except, track_cross_junctions): """Build elements in a pipeline""" @@ -248,7 +282,7 @@ def build(app, elements, all_, track_, track_save, track_all, track_except, trac ################################################################## @cli.command(short_help="Fetch sources in a pipeline") @click.option('--except', 'except_', multiple=True, - type=click.Path(dir_okay=False, readable=True), + type=ElementParamType(), help="Except certain dependencies from fetching") @click.option('--deps', '-d', default='plan', type=click.Choice(['none', 'plan', 'all']), @@ -258,7 +292,7 @@ def build(app, elements, all_, track_, track_save, track_all, track_except, trac @click.option('--track-cross-junctions', '-J', default=False, is_flag=True, help="Allow tracking to cross junction boundaries") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def fetch(app, elements, deps, track_, except_, track_cross_junctions): """Fetch sources required to build the pipeline @@ -299,7 +333,7 @@ def fetch(app, elements, deps, track_, except_, track_cross_junctions): ################################################################## @cli.command(short_help="Track new source references") @click.option('--except', 'except_', multiple=True, - type=click.Path(dir_okay=False, readable=True), + type=ElementParamType(), help="Except certain dependencies from tracking") @click.option('--deps', '-d', default='none', type=click.Choice(['none', 'all']), @@ -307,7 +341,7 @@ def fetch(app, elements, deps, track_, except_, track_cross_junctions): @click.option('--cross-junctions', '-J', default=False, is_flag=True, help="Allow crossing junction boundaries") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def track(app, elements, deps, except_, cross_junctions): """Consults the specified tracking branches for new versions available @@ -339,7 +373,7 @@ def track(app, elements, deps, except_, cross_junctions): @click.option('--remote', '-r', help="The URL of the remote cache (defaults to the first configured cache)") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def pull(app, elements, deps, remote): """Pull a built artifact from the configured remote artifact cache. @@ -368,7 +402,7 @@ def pull(app, elements, deps, remote): @click.option('--remote', '-r', default=None, help="The URL of the remote cache (defaults to the first configured cache)") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def push(app, elements, deps, remote): """Push a built artifact to a remote artifact cache. @@ -391,7 +425,7 @@ def push(app, elements, deps, remote): ################################################################## @cli.command(short_help="Show elements in the pipeline") @click.option('--except', 'except_', multiple=True, - type=click.Path(dir_okay=False, readable=True), + type=ElementParamType(), help="Except certain dependencies") @click.option('--deps', '-d', default='all', type=click.Choice(['none', 'plan', 'run', 'build', 'all']), @@ -403,7 +437,7 @@ def push(app, elements, deps, remote): type=click.STRING, help='Format string for each element') @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def show(app, elements, deps, except_, order, format_): """Show elements in the pipeline @@ -482,7 +516,7 @@ def show(app, elements, deps, except_, order, format_): @click.option('--isolate', is_flag=True, default=False, help='Create an isolated build sandbox') @click.argument('element', - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.argument('command', type=click.STRING, nargs=-1) @click.pass_obj def shell(app, element, sysroot, mount, isolate, build_, command): @@ -543,7 +577,7 @@ def shell(app, element, sysroot, mount, isolate, build_, command): @click.option('--hardlinks', default=False, is_flag=True, help="Checkout hardlinks instead of copies (handle with care)") @click.argument('element', - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.argument('directory', type=click.Path(file_okay=False)) @click.pass_obj def checkout(app, element, directory, force, integrate, hardlinks): @@ -577,7 +611,7 @@ def workspace(): @click.option('--track', 'track_', default=False, is_flag=True, help="Track and fetch new source references before checking out the workspace") @click.argument('element', - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.argument('directory', type=click.Path(file_okay=False)) @click.pass_obj def workspace_open(app, no_checkout, force, track_, element, directory): @@ -609,7 +643,7 @@ def workspace_open(app, no_checkout, force, track_, element, directory): @click.option('--all', '-a', 'all_', default=False, is_flag=True, help="Close all open workspaces") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def workspace_close(app, remove_dir, all_, elements): """Close a workspace""" @@ -655,7 +689,7 @@ def workspace_close(app, remove_dir, all_, elements): @click.option('--all', '-a', 'all_', default=False, is_flag=True, help="Reset all open workspaces") @click.argument('elements', nargs=-1, - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def workspace_reset(app, soft, track_, all_, elements): """Reset a workspace to its original state""" @@ -704,7 +738,7 @@ def workspace_list(app): ################################################################## @cli.command(name="source-bundle", short_help="Produce a build bundle to be manually executed") @click.option('--except', 'except_', multiple=True, - type=click.Path(dir_okay=False, readable=True), + type=ElementParamType(), help="Elements to except from the tarball") @click.option('--compression', default='gz', type=click.Choice(['none', 'gz', 'bz2', 'xz']), @@ -716,7 +750,7 @@ def workspace_list(app): @click.option('--directory', default=os.getcwd(), help="The directory to write the tarball to") @click.argument('element', - type=click.Path(dir_okay=False, readable=True)) + type=ElementParamType()) @click.pass_obj def source_bundle(app, element, force, directory, track_, compression, except_): -- cgit v1.2.1