Sublime Forum

Send arguments to custom Build system from plugin

#1

Hi,

Amateur developer here, I am trying to create a plugin which will basically run simple python script to get some data from web, generate temporary file from acquired data and open it in ST3. Basically, I don’t know how to pass custom argument to build command to run the script, for example, id of the webpage.

My .sublime-build file:
{
“selector”: “source.BugEditor”,
“working_dir” : “${file_path}”,
“variants”: [
{ “name”: “GetData”,
“cmd”: [ “${file_path}/get_data.py”, “${id}”]
}
}

And in plugin file I run it like this:

self.view.window().run_command(“build”, {“variant”: “GetData”,“id”: id})

However, I always get empty id when building:
[cmd: [’/home/md/.config/sublime-text-3/Packages/BugEditor/get_data.py’, ‘’]]

Is there any specific keyword which should be used to pass custom arguments?

Can anyone please give me some insight on this?

0 Likes

#2

As far as I’m aware, build systems don’t directly support arbitrary variable expansions such as what you’re trying to do here. Although the build command does not complain, if you give it arguments that it doesn’t understand, it just ignores them.

That said, the build command executes another command to actually perform the build, which is given by the target in the build system; when target is not specified, it defaults to exec.

If you’re not married to the idea of using a build system to do it, you can get the result you want by executing the exec command directly in your plugin instead of trying to pass it through the build system first. This page tells you the arguments that you can pass to the exec command, and the plugin referenced below shows how you can expand the standard variables along with your custom ones if you need to.

If you want/need to use a build system to do this, you can specify a target with a custom command that first performs the variable expansions for you and then invokes the exec command to actually perform the build. This sample plugin does something like this. It demonstrates using settings from your project or set in the currently active view and having them expand inside of the build before it executes.

Note that internally, the expansion of variables in the build system is done by the build command before it invokes the target. It does that on all documented keys in the sublime-build file, but leaves unknown keys alone. As a part of the variable expansion, any variables that it doesn’t know about get expanded to the empty string (which you’ve probably already noticed).

Thus by the time the command in the target gets executed, all of the variables have been expanded and your chance to process them has been lost. The plugin above gets around that by having you specify command instead of cmd in the sublime-build, so that Sublime will leave it alone, and then makes the modifications and renames the argument back to cmd.

Also, this does not directly allow you to pass the value of the variable as an argument to the build command. Instead, you have to store the value somewhere before you execute the build (e.g. in a temporary view setting) and then pull it out when the custom target executes.

You can sort of fake your way around that by also creating an EventListener that watches to see when you’re executing a build and then extracts the parameter and saves the value so that it will be visible to the above plugin. A simple example of that might be:

class MyEventListener(sublime_plugin.EventListener):
    def on_window_command(self, window, cmd, args):
        if (cmd == "build" and args is not None and "id" in args):
            if window.active_view() is not None:
                window.active_view().settings().set("id", args["id"])

Although you probably want to use a setting that has your package prefix on it or something to ensure that it doesn’t collide with an existing or future setting.

3 Likes

#3

Thanks a lot for this comprehensive answer! Indeed, I was overcomplicating this as there were no actual reason to use build system for this trivial task. I’ve managed to do everything with exec() and after some time switched to subprocess to work directly with script output.

In case anyone would need a quick example, you can use something like this in your plugins:

#build command array  in  any way you like
command = ["ls", "-a", "/home/"]  
# with exec()
cmd = {"cmd": command}
view.window().run_command("exec", cmd)
# with subprocess
import subprocess
process = subprocess.Popen(command, stdout=subprocess.PIPE)
out, err = process.communicate()
1 Like

#4

As explained here, expansion is only active for cmd, shell_cmd and working_dir.

Additionally, values in the env mapping have environment variables expanded by the exec default target.

1 Like

#5

Ah yeah, thanks for the clarification on that. :slight_smile: I knew it wasn’t all of the keys but I totally glossed over how the undocs mention a specific list.

0 Likes