CEP 11 - 定义 menuinst
标准
标题 | 定义 menuinst 标准 |
状态 | 已接受 |
作者 | Jaime Rodríguez-Guerra <jaime.rogue@gmail.com> |
创建于 | 2021 年 10 月 14 日 |
更新于 | 2023 年 7 月 28 日 |
讨论 | conda-incubator/ceps#8 |
实现 | conda/menuinst @cep-devel |
本文档中关键词 "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"NOT RECOMMENDED"、"MAY" 和 "OPTIONAL" 按照 RFC2119 中的描述进行解释,当且仅当它们全部以大写形式出现时,如此处所示。
摘要
menuinst
是 conda
使用的一个库,用于安装指向 conda
包提供的命令的菜单项。它的工作原理是在将包链接到环境后,发现位于 $PREFIX/Menu
中的某些 JSON 文件。
此库主要针对 Windows。最初的项目支持 Linux 和 MacOS,但 menuinst
从未在这些平台上实际使用过。因此,每个平台所需的 JSON 元数据差异很大,并且实现没有及时更新。
此 CEP 将尝试通过以下方式标准化 menuinst
接口:
- 为所有平台提供统一的元数据模式,以便单个文档包含在所有平台中创建快捷方式所需的所有元数据。
- 枚举不同配置的预期行为。
- 为实施者定义程序化接口(CLI / API)。
菜单元数据模式
完整的 JSON 模式在 此文档 中定义,但在此处您可以查看所有可能的键及其默认值的简化概述
{
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"$schema": "https://json-schema.fullstack.org.cn/draft-07/schema",
"menu_name": "REQUIRED",
"menu_items": [
{
"name": "REQUIRED",
"description": "REQUIRED",
"command": [
"REQUIRED",
],
"icon": None, # path to ico / png / icns file
"precreate": None, # command to run before the shortcut is created
"precommand": None, # command to run before activation and 'command'
"working_dir": None, # starting working location for the process
"activate": true, # activate conda environment before running 'command'
"terminal": false, # open in terminal and leave it open
"platforms": {
# To create the menu item for a fiven platform, the key must be present in this
# dictionary. Presence is enough; the value can just be the empty dictionary: {}.
"linux": {
# See XDG Desktop standard for details
# https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys
"Categories": None,
"DBusActivatable": None,
"GenericName": None,
"Hidden": None,
"Implements": None,
"Keywords": None,
"MimeType": None,
"NoDisplay": None,
"NotShowIn": None,
"OnlyShowIn": None,
"PrefersNonDefaultGPU": None,
"StartupNotify": None,
"StartupWMClass": None,
"TryExec": None,
#: Map of custom MIME types to their corresponding glob patterns (e.g. ``*.txt``).
"glob_patterns": None
},
"osx": {
# See Apple docs for CF* and LS* variables
# CF*: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# LS*: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html
"CFBundleDisplayName": None,
"CFBundleIdentifier": None,
"CFBundleName": None,
"CFBundleSpokenName": None,
"CFBundleVersion": None,
"CFBundleURLTypes": None,
"CFBundleDocumentTypes": None,
"LSApplicationCategoryType": None,
"LSBackgroundOnly": None,
"LSEnvironment": None,
"LSMinimumSystemVersion": None,
"LSMultipleInstancesProhibited": None,
"LSRequiresNativeExecution": None,
"UTExportedTypeDeclarations": None,
"UTImportedTypeDeclarations": None,
#: list of permissions to request for the app
#: see https://developer.apple.com/documentation/bundleresources/entitlements
"entitlements": None,
#: symlink a file (usually the executable in 'command') into the .app directory
"link_in_bundle": None,
#: shell logic that will run when an Apple event is received
"event_handler": None,
},
"win": {
"desktop": true, # create desktop location
"quicklaunch": true, # create quick launch shortcut too
"file_extensions": None, # file extensions to associate with shortcut in registry
"url_protocols": None, # URI protocols to associate with shortcut in registry
"app_user_model_id": None, # Identifier used to associate processes with a taskbar icon
}
}
}
]
}
请注意,每个 platforms
子字典(linux
、macos
、win
)都可以覆盖其 menu_items[*]
条目的全局值(例如,重新定义 command
以调整 shell 语法)。
每个 JSON 文件必须在构建时(例如在 conda-build
中)根据其 $id
模式进行验证。
占位符
每个平台必须提供这些占位符,以用于任何接受 str
类型的键的值。要替换它们,必须将它们包装在双花括号中:{{ NAME }}
。
占位符 | 值 |
---|---|
BASE_PREFIX | 基本 Python 位置的路径。在 conda 术语中,这是 base 环境 |
DISTRIBUTION_NAME | 基本前缀目录的名称;例如,如果 BASE_PREFIX 是 /opt/my-project ,则此值为 my-project 。 |
PREFIX | 目标 Python 位置的路径。在 conda 术语中,这是包含此菜单项的 JSON 文件的环境的路径。在某些情况下,它可能与 BASE_PREFIX 相同。 |
ENV_NAME | 与 DISTRIBUTION_NAME 相同,但用于 PREFIX 。 |
PYTHON | PREFIX 中 python 可执行文件的路径。 |
BASE_PYTHON | BASE_PREFIX 中 python 可执行文件的路径。 |
MENU_DIR | PREFIX 中 Menu 目录的路径。 |
MENU_ITEM_LOCATION | 主菜单项工件安装后的路径。在 Linux 上,这是 .desktop 文件的路径,在 macOS 上,这是 .app 目录的路径,在 Windows 上,这是“开始”菜单 .lnk 文件的路径。 |
BIN_DIR | PREFIX 中 bin (Unix) 或 Library/bin (Windows) 目录的路径。 |
PY_VER | PREFIX 中的 Python major.minor 版本。 |
SP_DIR | PREFIX 中 Python 的 site-packages 目录的路径。 |
HOME | 用户目录 (~ ) 的路径。 |
ICON_EXT | 每个平台期望的图标文件的扩展名。Linux 中为 png ,macOS 中为 icns ,Windows 中为 ico 。请注意,不包含点号。 |
仅限 macOS | |
PYTHONAPP | 安装在 PREFIX 中的 python 可执行文件的路径,假设已安装 python.app conda 包。等效于 {{ PREFIX }}/python.app/Contents/MacOS/python 。 |
仅限 Windows | |
SCRIPTS_DIR | PREFIX 中 Scripts 目录的路径。 |
BASE_PYTHONW | BASE_PREFIX 中 pythonw.exe 可执行文件的路径。 |
PYTHONW | PREFIX 中 pythonw.exe 可执行文件的路径。 |
打包指南
希望在安装时创建快捷方式的 conda
包必须提供一个 JSON 文件,使得
- JSON 内容必须通过模式验证。
- JSON 文件必须放置在
$PREFIX/Menu
下。 - JSON 文件名必须为
<package-name>.json
。 - 打包工具(例如
conda-build
)必须在创建包时检查是否满足上述条件。
一个正确放置的 JSON 文件的示例是包含在
my-package-1.2.3-h123abc.conda
工件中的$PREFIX/Menu/my-package.json
。
预期行为
每个平台必须将菜单工件放置在这些目标位置
操作系统 | 工件类型 | 用户位置 | 系统位置 | 备注 |
---|---|---|---|---|
Linux | .desktop 文件 | ~/.local/share/applications | /usr/local/share/applications | 修改了一些其他用户文件 |
macOS | .app 目录 | ~/Applications | /Applications | |
Windows | .lnk 文件 | “开始”菜单、桌面和/或快速启动栏内的 {{ menu_name }} 目录 | 开始菜单 | 这些位置是可自定义的,并在 Windows 注册表中配置。 |
- 在 Linux 上,几乎不需要做什么,因为 XDG 将责任委托给桌面管理器。实施者只需要创建
.desktop
文件并调整/添加菜单 XML 文件。 - 在 macOS 上,我们不得不提出一些想法。快捷方式实际上是一个
.app
目录。实施者必须遵守 Apple 的指南。有关实现细节,请参阅附录 B。 - 在 Windows 上,使用 Windows API 创建
.lnk
文件。文件类型和 URL 协议关联在 Windows 注册表 中完成。
某些安装可能提供两种模式:“仅当前用户”和“所有用户”。此选项未在 JSON 元数据中显示,但可能在 CLI 或 API 的创建时请求。这意味着实施者必须能够处理用户位置和系统位置,如上所述。特别是,需要考虑进程内权限提升。
删除包时,必须也删除文件工件。如果在其他资源(Linux 上的 XML 文件,Windows 上的注册表)中进行了更改,则也必须撤消这些更改。
CLI 接口
实施者 CLI 未在本文档中定义。但是,如果 constructor
的集成要继续使用,则应将其标准化。
建议的 CLI(灵感来自已使用的内容,以尽量减少更改)是
${IMPLEMENTER} constructor --prefix ${PREFIX} [--base-prefix ${BASE_PREFIX}] [--mode user|system] [--make-menus | --rm-menus] [pkg-name ...]
--make-menus
将为$PREFIX/Menu
中找到的 JSON 文件创建菜单项。--rm-menus
将从系统中卸载相应的菜单项。- 如果在这些两个标志旁边传递了值,则只会处理与这些包名称匹配的 JSON 文件。其他文件将被忽略。
--base-prefix
是可选的,默认为传递给--prefix
的值。仅当IMPLEMENTER
从--prefix
以外的位置运行时才需要它(例如,base
与自定义环境,或系统 Python 和虚拟环境)。--mode
是可选的,默认为--base-prefix
位置提供的标记。如果存在.nonadmin
文件,则将假定mode=user
。否则,将假定mode=system
,并在必要时回退到mode=user
。
或者,如果 IMPLEMENTER
在 create | install
命令中支持新设置和/或 CLI 标志,则可以删除 constructor
菜单所需的子命令。即
base_prefix
:覆盖假定的base
环境位置。如今,这可以作为root_prefix
使用,但通过环境变量(通过CONDA_ROOT_PREFIX
)覆盖它在conda
中存在错误,需要修复。- 扩展
shortcuts
以使其能够接受值(true、false 或字符串列表)。--shortcuts
将设置shortcuts=True
,否则这是默认值。--no-shorcuts
将设置shortcuts=False
。--shortcuts pkg1 pkg2 ...
将设置shortcuts=[pkg1, pkg2, ...]
,这将指示IMPLEMENTER
仅处理这些包的菜单项创建或删除。
向后兼容性
Windows 用户确实依赖于现有的 menuinst 1.x
“模式”。有很多包使用它。此(未版本化)文档需要保留并受到尊重。在缺少 $schema
或 $id
键的情况下,将假定元数据是使用旧模式构建的。
有关记录它的最佳尝试,请参见下面的附录 A。
参考文献
- 重新设计 linux/osx 支持以及新的简化格式?
- Mamba 在 C++ 中的实现
- conda、conda-standalone、constructor 和 menuinst 之间的交互
- 将 API 更改为
menuinst.install(path_or_dict)
- 截至 2021.10.18 的
menuinst
wiki - freedesktop.org 规范
- 核心基础密钥 (info.plist)
- Windows 中的文件类型关联
- Windows 中的默认程序
版权
所有 CEP 均明确声明为 CC0 1.0 Universal。
附录 A
menuinst 1.x
预标准
每个平台所需的元数据记录在 menuinst
wiki 中。但是,该工具实际上只支持 Windows。这种不对称的增长使 Windows 能够发展出一种临时的规范,这种规范实际上不能很好地转化为其他平台。
总体模式似乎是
{
"menu_name": str,
"menu_items": list of dict,
}
不幸的是,每个菜单项字典(我们称之为 MenuItem
)在每个平台中都采用不同的形式。
Windows 上的 MenuItem
{
["system" | "script" | "pyscript" | "pywscript" | "webbrowser"]: str,
"scriptargument": str,
"scriptarguments": list of str,
"name": str,
"workdir": str,
"icon": str,
"desktop": bool,
"quicklaunch": bool,
}
当前允许的占位符是
${PREFIX}
:Python 环境前缀${ROOT_PREFIX}
:root(conda 或其他方式)安装的 Python 环境前缀${PYTHON_SCRIPTS}
:Python 环境中的 Scripts 文件夹,${PREFIX}/Scripts
${MENU_DIR}
:菜单配置和图标文件的文件夹,${PREFIX}/Menu
${PERSONALDIR}
:不确定${USERPROFILE}
:用户的 home 文件夹${ENV_NAME}
:此快捷方式所在的环境。${DISTRIBUTION_NAME}
:root 前缀的文件夹名称,例如,如果发行版安装在“C:\Users\Miniconda”,则为“Miniconda”。${PY_VER}
:仅 Python 主版本。这取自 root 前缀。通常用于将快捷方式放置在父安装的菜单下。${PLATFORM}
:(32 位) 或 (64 位) 之一。这取自 root 前缀。通常用于将快捷方式放置在父安装的菜单下。
MacOS 上的 MenuItem
{
"cmd": str,
"name": str,
"icns": str,
}
当前允许的占位符是
${BIN_DIR}
:PREFIX/bin
${MENU_DIR}
:PREFIX/Menu
Linux 上的 MenuItem
{
"cmd": list of str,
"id": str,
"name": str,
"comment": str.
"terminal": bool,
"icon": str,
},
在 Linux 上,只有 cmd
可以接受两个特殊占位符 {{FILEBROWSER}}
和 {{WEBBROWSER}}
,它们分别由默认的桌面文件资源管理器和默认的 Web 浏览器替换。
已识别的问题
command
接口
Windows 有几种方法来指定应使用快捷方式运行哪个命令
system
+scriptargument[s]
:可执行文件的路径及其参数。script
+scriptargument[s]
:与上述相同,但可执行文件在调用ROOT_PYTHON cwp.py PREFIX
后在子进程中运行。pyscript
:将script
硬编码为PREFIX/python.exe
,并将该值作为第一个(也是唯一的)参数。pywscript
:与上述相同,但使用pythonw.exe
作为启动器,理论上是为了避免在应用程序旁边启动控制台窗口。webbrowser
:PREFIX/python -m webbrowser -t URL
的别名。
在 Linux 上,命令由 cmd
指定,表示为字符串列表。在 MacOS 上,也接受 cmd
,但在这种情况下,它应该是一个原始字符串。
icon
键
Windows 和 Linux 期望 icon
。MacOS 期望 icns
。每个平台都需要不同的文件格式,但这可以使用占位符来安排。
标准化占位符
允许的占位符在不同平台之间差异很大。必须识别并实现一个通用的子集。仅在绝对必要时才允许使用特定于平台的选项。
附录 B:macOS 中的实现细节
- 大多数 macOS 特定的设置都映射到
.app
的Info.plist
键值对。 - 带有
precommand
+activate
+command
逻辑的 shell 脚本位于<NAME>.app/Contents/MacOS/<NAME>-script
中。 - 系统集成需要二进制启动器(请参阅原因
conda/menuinst#123
)。它位于<NAME>.app/Contents/MacOS/<NAME>
。建议的启动器只是猜测其自身的位置以找到*-script
文件,该文件在子进程中生成。 - 在某些情况下,如果需要外部二进制文件,则需要将其符号链接到
.app
目录中,以确保键盘集成工作(请参阅conda/menuinst#122
)。 - URL 协议关联需要在二进制启动器中提供特殊支持。实施者可以选择如何实现它。有关想法,请参阅 此问题 和 此 PR。