Build Systems
Ver
:
Sublime Text provides build systems to allow users to run external programs. Examples of common uses for build systems include: compiling, transpiling, linting, and executing tests.
Build systems are specified via JSON and saved in a file with the
extension .sublime-build. A new build system can be created by
the menu item or the Build:
New Build System
command palette entry.
Build systems have various ways they can associate themselves with files and
projects. Using this information, Sublime Text can intelligently display only
viable build systems to the user. The built-in exec
target
provides common options to get up and running quickly. For more complex
requirements, build systems can target custom Sublime Text commands written in
Python.
Basic Exampleπ
The following is a basic example of a build system. This build system will execute the currently-open Python file.
{
"cmd": ["python", "$file"],
"selector": "source.python",
"file_regex": "^\\s*File \"(...*?)\", line ([0-9]*)"
}
The Usage and Options sections will discuss how to use and customize a build system.
Usageπ
Build systems include the following functionality:
Automatic selection of a build system based on file type
Remembering the last used build system
Navigation of build system results
Ability to cancel a build
Running a Buildπ
A build can be run by one of the following methods:
Keyboard |
Menu |
||
---|---|---|---|
Windows/Linux |
Mac |
All |
|
Ctrl+B |
β+B |
F7 |
Output will be shown in an output panel displayed at the bottom of the Sublime Text window.
Selecting a Build Systemπ
By default, Sublime Text uses automatic selection of build systems. When a user invokes a build, the current fileβs syntax and filename will be used to pick the appropriate build system.
If more than one build system matches the current file type, the user will be prompted to pick the build system they wish to use. Once a build system has been selected, Sublime Text will remember it until the user changes their selection.
To manually choose a build system, use:
To change the build system, within the viable options, use one of the following methods:
Keyboard |
Menu |
Command Palette |
|
---|---|---|---|
Windows/Linux |
Mac |
|
|
Ctrl+Shift+B |
β§+β+B |
Cancelling a Buildπ
An in-process build can be cancelled via:
Keyboard |
Menu |
Command Palette |
|
---|---|---|---|
Windows/Linux |
Mac |
|
|
Ctrl+Break |
Ctrl+C |
Optionsπ
All build systems may use the following top-level keys in the .sublime-build file:
- "selector" stringπ
A selector to match the syntax that this build system should be enabled for.
Example:
"source.python"
- "file_patterns" array of stringsπ
Patterns of file names the build system should be enabled for.
Example:
["*.py"]
- "keyfiles" array of stringsπ
File names, if present in one of the opened folders, that will cause the build system to be enabled.
Example:
["Makefile"]
- "variants" array of objectsπ
Subsidiary build systems that will inherit the options from the top-level build system. Each variant needs to specify a
"name"
key, and may override or add options to the top-level build system.Example:
[ { "name": "Debug Symbols", "cmd": ["my_command", "-D", "$file"] } ]
- "cancel" string, array of stringsπ
A string command name, or an array of string options.
If a string is specified, the command specified will be used to cancel the build.
If an array of strings, the primary
"target"
will be called, with these options added on. This only needs to be specified when using a custom"target"
.Examples:
"cancel_my_build"
or{ "kill": true }
- "target" stringπ
The command to run when the build system is invoked. The default value of
exec
allows use of the additional options specified in exec Target Options.If a value other than
"exec"
is specified, none of the options in exec Target Options will do anything.See the Advanced Example for a complete example.
Example:
"my_build"
- "windows" objectπ
Options to use when the build system is being executed on a Windows machine.
Example:
{ "cmd": ["my_command.exe", "/D", "$file"] }
- "osx" objectπ
Options to use when the build system is being executed on a Mac machine.
Example:
{ "cmd": ["/Applications/MyProgram.app/Contents/MacOS/my_command", "-d", "$file"] }
- "linux" objectπ
Options to use when the build system is being executed on a Linux machine.
Example:
{ "cmd": ["/usr/local/bin/my_command", "-d", "$file"] }
exec
Target Optionsπ
The default target
of exec
is used by the majority of build systems. It
provides the following options to control what program to execute, and how to
display the results.
- "cmd" array of stringsπ
The executable to run, plus any arguments to pass to it. Shell constructs such as piping and redirection are not supported β see
"shell_cmd"
.May use variables.
Example:
["my_command", "-d", "$file"]
- "shell_cmd" stringπ
A shell command to execute. Unlike the
"cmd"
option, this does allow piping and redirection. Will usebash
on Mac and Linux machine, andcmd.exe
on Windows.This takes precedence over
"cmd"
. If you want to override the"shell_cmd"
in a build variant with a"cmd"
then also set the"shell_cmd"
tonull
.May use variables.
Example:
"my_command \"$file\" | other_command"
- "working_dir" stringπ
The directory to execute the
"cmd"
or"shell_cmd"
within.May use variables.
Example:
"$file_path"
- "file_regex" stringπ
A regular expression to run on the build output to match file information. The matched file information is used to enable result navigation. The regex should capture 2, 3 or 4 groups.
The capture groups should be:
filename
line number
column number
message
Example:
"^\s*(\\S[^:]*)\\((\\d+):(\\d+)\\): ([^\\n]+)"
- "line_regex" stringπ
A regular expression to run on the build output to match line information. The matched file information is used to enable result navigation. The regex should capture 1, 2 or 3 groups.
The groups should capture:
line number
column number
error message
This regular expression is only necessary when some results contain strictly a line number, line and column numbers, or line and column numbers with a message. When such a match is made, the
"file_regex"
option will be used to search backwards to find the appropriate file name.Example:
"^\s*line (\\d+) col (\\d+): ([^\\n]+)"
- "encoding" stringπ
The encoding of the build system output. Uses Python codec names. Defaults to
"utf-8"
.Example:
"iso-8859-1"
- "env" objectπ
Environment variable values to use when running the
"cmd"
or"shell_cmd"
.Example:
{ "PYTHONIOENCODING": "utf-8" }
- "quiet" booleanπ
Reduces the amount of output about the build system invocation.
Example:
true
- "word_wrap" booleanπ
Turns on word wrapping in the build system output panel.
Example:
true
- "syntax" stringπ
The syntax file to use to highlight the build system output panel.
Example:
"Packages/JavaScript/JSON.sublime-syntax"
Custom Optionsπ
When implementing a command to act as a build system target, the commandβs keyword arguments are available via options in the .sublime-build file. However, certain parameter names will not work since they conflict with built-in build system functionality.
The following names will not be passed as arguments to commands. This also
applies to other situations, such as options specified in the "cancel"
,
"linux"
, "osx"
and "windows"
options.
"cancel"
"file_patterns"
"keyfile"
"keyfiles"
"linux"
"osx"
"save_untitled_files"
"selector"
"target"
"variants"
"windows"
Variablesπ
The following variables will be expanded within any string specified in the
"cmd"
, "shell_cmd"
or "working_dir"
options.
If a literal $
needs to be specified in one of these options, it must be
escaped with a \
. Since JSON uses backslashes for escaping also, $
will
need to be written as \\$
.
Please note that this substitution will occur for any <span
class=βkeyβ>βtargetβ</span>. If a custom target is used, it may implement
variable expansion for additional options by using
sublime.expand_variables()
with the result from
self.window.extract_variables()
. </p>
Variable |
Description |
---|---|
|
The path to the Packages/ folder. |
|
The platform Sublime Text is running on: |
|
The full path, including folder, to the file in the active view. |
|
The path to the folder that contains the file in the active view. |
|
The file name (sans folder path) of the file in the active view. |
|
The file name, exluding the extension, of the file in the active view. |
|
The extension of the file name of the file in the active view. |
|
The full path to the first folder listed in the side bar. |
|
The full path to the current project file. |
|
The path to the folder containing the current project file. |
|
The file name (sans folder path) of the current project file. |
|
The file name, excluding the extension, of the current project file. |
|
The extension of the current project file. |
Advanced Exampleπ
The following example shows a custom target
command, with the ability to
cancel a build and navigate results.
A target
for a build system should be a sublime_plugin.WindowCommand
. This
will provide the instance variable of self.window
to allow interaction with
the current project, window and active view.
Please note that the following example is somewhat simplistic in its implementation, and it wonβt handle many common edge cases.
The following Python can be saved to a file named Package/User/my_example_build.py:
import sublime
import sublime_plugin
import subprocess
import threading
import os
class MyExampleBuildCommand(sublime_plugin.WindowCommand):
encoding = 'utf-8'
killed = False
proc = None
panel = None
panel_lock = threading.Lock()
def is_enabled(self, lint=False, integration=False, kill=False):
# The Cancel build option should only be available
# when the process is still running
if kill:
return self.proc is not None and self.proc.poll() is None
return True
def run(self, lint=False, integration=False, kill=False):
if kill:
if self.proc:
self.killed = True
self.proc.terminate()
return
vars = self.window.extract_variables()
working_dir = vars['file_path']
# A lock is used to ensure only one thread is
# touching the output panel at a time
with self.panel_lock:
# Creating the panel implicitly clears any previous contents
self.panel = self.window.create_output_panel('exec')
# Enable result navigation. The result_file_regex does
# the primary matching, but result_line_regex is used
# when build output includes some entries that only
# contain line/column info beneath a previous line
# listing the file info. The result_base_dir sets the
# path to resolve relative file names against.
settings = self.panel.settings()
settings.set(
'result_file_regex',
r'^File "([^"]+)" line (\d+) col (\d+)'
)
settings.set(
'result_line_regex',
r'^\s+line (\d+) col (\d+)'
)
settings.set('result_base_dir', working_dir)
self.window.run_command('show_panel', {'panel': 'output.exec'})
if self.proc is not None:
self.proc.terminate()
self.proc = None
args = ['my_cli']
if lint:
args.append('-l')
elif integration:
args.append('-i')
args.append(vars['file_name'])
self.proc = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=working_dir
)
self.killed = False
threading.Thread(
target=self.read_handle,
args=(self.proc.stdout,)
).start()
def read_handle(self, handle):
chunk_size = 2 ** 13
out = b''
while True:
try:
data = os.read(handle.fileno(), chunk_size)
# If exactly the requested number of bytes was
# read, there may be more data, and the current
# data may contain part of a multibyte char
out += data
if len(data) == chunk_size:
continue
if data == b'' and out == b'':
raise IOError('EOF')
# We pass out to a function to ensure the
# timeout gets the value of out right now,
# rather than a future (mutated) version
self.queue_write(out.decode(self.encoding))
if data == b'':
raise IOError('EOF')
out = b''
except (UnicodeDecodeError) as e:
msg = 'Error decoding output using %s - %s'
self.queue_write(msg % (self.encoding, str(e)))
break
except (IOError):
if self.killed:
msg = 'Cancelled'
else:
msg = 'Finished'
self.queue_write('\n[%s]' % msg)
break
def queue_write(self, text):
sublime.set_timeout(lambda: self.do_write(text), 1)
def do_write(self, text):
with self.panel_lock:
self.panel.run_command('append', {'characters': text})
The custom MyExampleBuildCommand
can be configured as a build system using
the following JSON saved to a file named Packages/User/My Example
Build.sublime-build:
{
"target": "my_example_build",
"selector": "source.mylang",
"cancel": {"kill": true},
"variants": [
{
"name": "Lint",
"lint": true
},
{
"name": "Integration Tests",
"integration": true
}
]
}