跳到主要内容

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 中的描述进行解释,当且仅当它们全部以大写形式出现时,如此处所示。

摘要

menuinstconda 使用的一个库,用于安装指向 conda 包提供的命令的菜单项。它的工作原理是在将包链接到环境后,发现位于 $PREFIX/Menu 中的某些 JSON 文件。

此库主要针对 Windows。最初的项目支持 Linux 和 MacOS,但 menuinst 从未在这些平台上实际使用过。因此,每个平台所需的 JSON 元数据差异很大,并且实现没有及时更新。

此 CEP 将尝试通过以下方式标准化 menuinst 接口:

  1. 为所有平台提供统一的元数据模式,以便单个文档包含在所有平台中创建快捷方式所需的所有元数据。
  2. 枚举不同配置的预期行为。
  3. 为实施者定义程序化接口(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 子字典(linuxmacoswin)都可以覆盖其 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_NAMEDISTRIBUTION_NAME 相同,但用于 PREFIX
PYTHONPREFIXpython 可执行文件的路径。
BASE_PYTHONBASE_PREFIXpython 可执行文件的路径。
MENU_DIRPREFIXMenu 目录的路径。
MENU_ITEM_LOCATION主菜单项工件安装后的路径。在 Linux 上,这是 .desktop 文件的路径,在 macOS 上,这是 .app 目录的路径,在 Windows 上,这是“开始”菜单 .lnk 文件的路径。
BIN_DIRPREFIXbin (Unix) 或 Library/bin (Windows) 目录的路径。
PY_VERPREFIX 中的 Python major.minor 版本。
SP_DIRPREFIX 中 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_DIRPREFIXScripts 目录的路径。
BASE_PYTHONWBASE_PREFIXpythonw.exe 可执行文件的路径。
PYTHONWPREFIXpythonw.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

或者,如果 IMPLEMENTERcreate | 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。

参考文献

所有 CEP 均明确声明为 CC0 1.0 Universal


附录 A

每个平台所需的元数据记录在 menuinst wiki 中。但是,该工具实际上只支持 Windows。这种不对称的增长使 Windows 能够发展出一种临时的规范,这种规范实际上不能很好地转化为其他平台。

总体模式似乎是

{
"menu_name": str,
"menu_items": list of dict,
}

不幸的是,每个菜单项字典(我们称之为 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 前缀。通常用于将快捷方式放置在父安装的菜单下。
{
"cmd": str,
"name": str,
"icns": str,
}

当前允许的占位符是

  • ${BIN_DIR}PREFIX/bin
  • ${MENU_DIR}PREFIX/Menu
{
"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 作为启动器,理论上是为了避免在应用程序旁边启动控制台窗口。
  • webbrowserPREFIX/python -m webbrowser -t URL 的别名。

在 Linux 上,命令由 cmd 指定,表示为字符串列表。在 MacOS 上,也接受 cmd,但在这种情况下,它应该是一个原始字符串。

icon

Windows 和 Linux 期望 icon。MacOS 期望 icns。每个平台都需要不同的文件格式,但这可以使用占位符来安排。

标准化占位符

允许的占位符在不同平台之间差异很大。必须识别并实现一个通用的子集。仅在绝对必要时才允许使用特定于平台的选项。

附录 B:macOS 中的实现细节

  • 大多数 macOS 特定的设置都映射到 .appInfo.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