Sublime Forum

Builder variable for portable mode

#1

Hi.
I was overjoyed when I tried st.
So much that I registered within a week, great tool.
Even more so when I discovered that it can operate as a portable app.
It’s highly convenient to be able to carry around a portable editor/sdk/
toollchain.
I have a feature request though:

Please provide support for a run-time variable that expands/evaluates
to the local drive that sb itself is installed on.

Use case:
St user has a portable toolchain on a removable drive,
let’s assume drive_X:\my-toolchain\etc*.exe
Unfortunately, since we are on a portable drive,
drive_X: letter assignment will change when moving the drive around.
St itself is installed as a portable app on the same removable drive,
and has to somehow point the builder(s) to the above toolchain.

Currently st user has to either:
a) adjust system/user path, or
b) edit/hard-code full paths on builder config files according to a changing drive letter.

Proposed solution/enhancement:

Provide an st variable (lets say $DriveLetter),
that is evaluated on st start-up and corresponds to the drive letter
st itself in installed on.
The variable is to be expanded on evaluation of builder files
to substitute the changing drive letter of a removable drive.

Now the user can use $DriveLetter:\my-toolchain\etc*.exe on builder files,
and the builder system will pick-up the ever-changing paths of the removable drive.

It would be really helpful when operating in portable mode.

Thank you.

2 Likes

#2

You can set this up by creating your own custom build target that expands your variables out for you.

This example is based on this sample plugin that shows one method for including your own custom build variables as part of a build system.

You can choose Tools > Developer > New Plugin... from the menu, then replace the stub code with the plugin below and save in the default location that Sublime prompts you with as e.g. portable_exec.py:

import sublime, sublime_plugin
import os

class PortableExecCommand(sublime_plugin.WindowCommand):
    def createExecDict(self, sourceDict):
        # Get the drive Sublime is running from
        drive = os.path.splitdrive(sublime.executable_path())[0]

        # Variables to expand; start with defaults, then add ours.
        variables = self.window.extract_variables ()
        variables["DriveLetter"] = drive

        # Create arguments to return by expanding variables in the
        # arguments given.
        args = sublime.expand_variables (sourceDict, variables)

        # Rename the command parameter to what exec expects.
        args["cmd"] = args.pop ("command", None)
        args["shell_cmd"] = args.pop("shell_command", None)

        return args

    def run(self, **kwargs):
        self.window.run_command ("exec", self.createExecDict (kwargs))

To use this you need to add a target to your sublime-build file that tells Sublime to run a different command than the default exec command. Additionally, you need to use command instead of cmd or shell_command instead of shell_cmd to specify what is actually being executed (explained below).

Example build files might be:

{
    "target": "portable_exec",
    "shell_command": "echo $DriveLetter"
}
{
    "target": "portable_exec",
    "command": ["echo", "$DriveLetter"],
    "shell": true
}

This works because the target option tells Sublime to use our portable_exec command instead of the standard exec (which is the default if target is not provided). Our command expands out variables including the desired $DriveLetter variable and then executes the standard exec command to do the actual work.

It’s important that you use command or shell_command instead of cmd or shell_cmd because internally when executing the build command, Sublime will expand all of the variables in those keys before it invokes the custom target command. As a consequence of that, the $DriveLetter variable is expanded out to be empty.

To get around this the plugin takes slightly different options that Sublime will ignore, and then renames them to the appropriate value before executing the command.

4 Likes

#3

Thanks for that.
I’ll try it next week and report back.
Still, a system variable could simplify things,
so I ask the devs to consider the possibility adding it.

1 Like

#4

I had some time and tried it.
Close but not there yet.

I created “portable_exec.py” as per the example.

Then I created a new build variant:

{
        "name": "Run portable",
        "target": "portable_exec",
        "command": ["echo", "$DriveLetter", "\\rebol-sdk\\tools\\rebcmdview.bat", "-w", "$file"],
        "shell": true
    }

When I call the new build variant it replaces the introduced $DriveLetter with the correct drive letter,
but it introduces a space between drive letter and the rest of the path:

G: \rebol-sdk\tools\rebcmdview.bat -w C:\Users\johndoe\Projects\scratch.r
[Finished in 0.2s]

The rest of the variables/switches seem ok.
Since I don’t speak Python, could you please advice?
Thanks.

0 Likes

#5

When you use cmd (or in this case command), you’re specifying an array of command line arguments one at a time, so above you’re telling it that the first argument to the echo command is the drive letter, then the second argument is a partial path to a batch file, and so on.

The easiest fix would be to put the drive letter inside the argument with the rest of the path (as a variable it can appear anywhere):

{
        "name": "Run portable",
        "target": "portable_exec",
        "command": ["echo", "$DriveLetter\\rebol-sdk\\tools\\rebcmdview.bat", "-w", "$file"],
        "shell": true
}

Alternatively you can use shell_command directly, in which case you specify a string exactly as you would enter the command on the command line, which allows you to specify the command in a more natural way:

{
        "name": "Run portable",
        "target": "portable_exec",
        "shell_command": "echo $DriveLetter\\rebol-sdk\\tools\\rebcmdview.bat -w \"$file\""
}

Here the $file is wrapped in explicit double quotes to ensure that things don’t go pear shaped if you happen to be working with a file that has a space somewhere in its name. That’s something that using cmd takes care of for you because it is explicitly specifying what each argument represents.

2 Likes

#6

Oh…
That works :smiley:
You sir, are my hero.
Thanks for taking the time to explain to me.

1 Like