构建系统
版本
Sublime Text 提供了*构建系统*,允许用户运行外部程序。构建系统的常见用途包括:编译、转译、代码检查和执行测试。
构建系统通过 JSON 指定,并保存在扩展名为 .sublime-build 的文件中。可以通过 菜单项或 构建: 新建 构建 系统
命令面板条目创建新的构建系统。
构建系统可以通过多种方式与文件和项目相关联。利用这些信息,Sublime Text 可以智能地只向用户显示可行的构建系统。内置的 exec
目标提供了一些常用选项,可以快速启动和运行。对于更复杂的需求,构建系统可以以用 Python 编写的自定义 Sublime Text 命令为目标。
基本示例🔗
以下是一个构建系统的基本示例。此构建系统将执行当前打开的 Python 文件。
{
"cmd": ["python", "$file"],
"selector": "source.python",
"file_regex": "^\\s*File \"(...*?)\", line ([0-9]*)"
}
用法🔗
构建系统包括以下功能
根据文件类型自动选择构建系统
记住上次使用的构建系统
导航构建系统结果
能够取消构建
运行构建🔗
可以通过以下方法之一运行构建
键盘 |
菜单 |
||
---|---|---|---|
Windows/Linux |
Mac |
所有 |
|
Ctrl+B |
⌘+B |
F7 |
输出将显示在 Sublime Text 窗口底部显示的输出面板中。
选择构建系统🔗
默认情况下,Sublime Text 使用自动选择构建系统。当用户调用构建时,将使用当前文件的语法和文件名来选择合适的构建系统。
如果有多个构建系统与当前文件类型匹配,则会提示用户选择要使用的构建系统。选择构建系统后,Sublime Text 会记住它,直到用户更改选择。
要手动选择构建系统,请使用:
要更改构建系统,请在可行选项中使用以下方法之一
键盘 |
菜单 |
命令面板 |
|
---|---|---|---|
Windows/Linux |
Mac |
|
|
Ctrl+Shift+B |
⇧+⌘+B |
取消构建🔗
可以通过以下方式取消正在进行的构建
键盘 |
菜单 |
命令面板 |
|
---|---|---|---|
Windows/Linux |
Mac |
|
|
Ctrl+Break |
Ctrl+C |
选项🔗
所有构建系统都可以在 .sublime-build 文件中使用以下顶级键
- "keyfiles" 字符串 数组🔗
如果在打开的文件夹中存在这些文件名,则将启用构建系统。
例如:
["Makefile"]
- "variants" 对象 数组🔗
将从顶级构建系统继承选项的子级构建系统。每个变体都需要指定一个
"name"
键,并且可以覆盖或添加顶级构建系统的选项。例如
[ { "name": "Debug Symbols", "cmd": ["my_command", "-D", "$file"] } ]
- "cancel" 字符串, 字符串 数组🔗
字符串命令名称或字符串选项数组。
如果指定了字符串,则将使用指定的命令取消构建。
如果指定了字符串数组,则将调用主
"target"
,并将这些选项添加进去。仅在使用自定义"target"
时才需要指定此项。例如:
"cancel_my_build"
或{ "kill": true }
- "target" 字符串🔗
调用构建系统时要运行的命令。默认值
exec
允许使用 exec 目标选项 中指定的附加选项。如果指定了
"exec"
以外的值,则 exec 目标选项 中的任何选项都不会起作用。有关完整示例,请参见高级示例。
例如:
"my_build"
- "windows" 对象🔗
在 Windows 机器上执行构建系统时要使用的选项。
例如
{ "cmd": ["my_command.exe", "/D", "$file"] }
- "osx" 对象🔗
在 Mac 机器上执行构建系统时要使用的选项。
例如
{ "cmd": ["/Applications/MyProgram.app/Contents/MacOS/my_command", "-d", "$file"] }
- "linux" 对象🔗
在 Linux 机器上执行构建系统时要使用的选项。
例如
{ "cmd": ["/usr/local/bin/my_command", "-d", "$file"] }
exec
目标选项🔗
默认的 target
exec
被大多数构建系统使用。它提供了以下选项来控制要执行的程序以及如何显示结果。
- "cmd" 字符串 数组🔗
要运行的可执行文件,以及要传递给它的任何参数。不支持诸如管道和重定向之类的 Shell 结构 - 请参见
"shell_cmd"
。可以使用变量。
例如:
["my_command", "-d", "$file"]
- "shell_cmd" 字符串🔗
要执行的 shell 命令。 与
"cmd"
选项不同,这允许管道和重定向。 将在 Mac 和 Linux 机器上使用bash
,在 Windows 上使用cmd.exe
。这优先于
"cmd"
。 如果要使用"cmd"
覆盖构建变体中的"shell_cmd"
,则还要将"shell_cmd"
设置为null
。可以使用变量。
示例:
"my_command \"$file\" | other_command"
- "working_dir" 字符串🔗
执行
"cmd"
或"shell_cmd"
的目录。可以使用变量。
示例:
"$file_path"
- "file_regex" 字符串🔗
要在构建输出上运行以匹配文件信息的正则表达式。 匹配的文件信息用于启用结果导航。 正则表达式应捕获 2、3 或 4 个组。
捕获组应该是
文件名
行号
列号
消息
示例:
"^\s*(\\S[^:]*)\\((\\d+):(\\d+)\\): ([^\\n]+)"
- "line_regex" 字符串🔗
要在构建输出上运行以匹配行信息的正则表达式。 匹配的文件信息用于启用结果导航。 正则表达式应捕获 1、2 或 3 个组。
这些组应该捕获
行号
列号
错误信息
仅当某些结果严格包含行号、行号和列号,或带有消息的行号和列号时,才需要此正则表达式。 当进行此类匹配时,将使用
"file_regex"
选项向后搜索以找到相应的文件名。示例:
"^\s*line (\\d+) col (\\d+): ([^\\n]+)"
- "encoding" 字符串🔗
构建系统输出的编码。 使用 Python 编解码器名称。 默认为
"utf-8"
。示例:
"iso-8859-1"
- "env" 对象🔗
运行
"cmd"
或"shell_cmd"
时要使用的环境变量值。例如
{ "PYTHONIOENCODING": "utf-8" }
- "quiet" 布尔值🔗
减少有关构建系统调用的输出量。
示例:
true
- "word_wrap" boolean🔗
在构建系统输出面板中启用自动换行。
示例:
true
- "syntax" string🔗
用于高亮显示构建系统输出面板的语法文件。
示例:
"Packages/JavaScript/JSON.sublime-syntax"
自定义选项🔗
当实现一个命令作为构建系统目标时,该命令的关键字参数可以通过 .sublime-build 文件中的选项来使用。但是,某些参数名称将不起作用,因为它们与内置的构建系统功能冲突。
以下名称将不会作为参数传递给命令。这也适用于其他情况,例如在 "cancel"
、"linux"
、"osx"
和 "windows"
选项中指定的选项。
"cancel"
"file_patterns"
"keyfile"
"keyfiles"
"linux"
"osx"
"save_untitled_files"
"selector"
"target"
"variants"
"windows"
变量🔗
以下变量将在 "cmd"
、"shell_cmd"
或 "working_dir"
选项中指定的任何字符串内展开。
如果需要在其中一个选项中指定一个字面量 $
,则必须使用 \
对其进行转义。由于 JSON 也使用反斜杠进行转义,因此 $
需要写成 \\$
。
请注意,此替换将发生在任何 <span class=”key”>“target”</span> 中。如果使用自定义目标,它可以通过对 sublime.expand_variables()
使用 self.window.extract_variables()
的结果来实现对其他选项的变量扩展。 </p>
变量 |
描述 |
---|---|
|
Packages/ 文件夹的路径。 |
|
Sublime Text 运行的操作系统: |
|
活动视图中文件的完整路径,包括文件夹。 |
|
包含活动视图中文件的文件夹的路径。 |
|
活动视图中文件的文件名(不含文件夹路径)。 |
|
活动视图中文件的文件名,不包括扩展名。 |
|
活动视图中文件的文件名的扩展名。 |
|
侧边栏中列出的第一个文件夹的完整路径。 |
|
当前项目文件的完整路径。 |
|
包含当前项目文件的文件夹的路径。 |
|
当前项目文件的文件名(不含文件夹路径)。 |
|
当前项目文件的文件名,不包括扩展名。 |
|
当前项目文件的扩展名。 |
高级示例🔗
以下示例显示了一个自定义的 target
命令,它能够取消构建并导航结果。
构建系统的 target
应该是一个 sublime_plugin.WindowCommand
。这将提供 self.window
的实例变量,以便与当前项目、窗口和活动视图进行交互。
请注意,以下示例的实现方式有些简单,它不会处理许多常见的边缘情况。
以下 Python 代码可以保存到名为 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})
自定义的 MyExampleBuildCommand
可以配置为构建系统,使用以下 JSON 保存到名为 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
}
]
}