当前位置: 首页 > news >正文

通过 Nix 管理 C 和 C++ 依赖项

大家好!我是大聪明-PLUS

我使用 Nix 已经有一段时间了。它是一款功能强大的工具,可以通过自动化开发环境管理,大大简化开发人员的工作。

今天我想仔细研究一下 Nix 最有用的部分:从 nixpkgs 存储库管理软件包

我曾经使用过多种语言,并且仍在继续使用。Rust 有一个很棒的包管理器叫 cargo,还有一个安装程序叫 rustup;JavaScript 有 npm。在 Python 的世界里,我也喜欢 conda。

我总是在 C 和 C++ 项目中遇到类似的问题。这些语言的包管理器常常让人很失望。即使它们能正常工作,它们的仓库也可能缺少必要的库。即使一切看起来都运行良好,但最终实现二进制缓存可能也需要付出努力,而当涉及到不同版本的 Qt 时,在开发人员的机器上编译所有内容会非常麻烦。

我希望该仪器无需任何额外调整就能发挥最大功效。

因此,我将向您展示如何使用 Nix 作为 C 和 C++ 的包管理器。


为什么是 Nix?

  • Nix 软件包的 nixpkgs 存储库包含超过100,000 个用于构建各种程序和库的配方。

  • 它们大多数是用 C 和 C++ 编写的。这意味着任何流行的库或实用程序可能都已经定义好了,要使用它,你只需要添加依赖项。

  • 在 C 和 C++ 世界中,管理依赖关系是一件令人头疼的事情;Nix 几乎完全解决了这个问题,让您可以专注于最重要的事情——编写生产代码。

  • Nix 并不与特定语言绑定:编译器、linters 和各种工具几乎肯定已经在 nixpkgs 中,因此添加新语言只是添加依赖项。

例如,我编写了一个 Bash 脚本,并使用 Nix 对其进行了“编译”,以确保在全新 Ubuntu 安装中可能不存在的实用程序的存在。即使是简单的脚本也存在依赖项:Nix 允许您定义例如 [[your_username] jq] ,并确保此工具所需的版本在您的 PATH 中。

开发人员容器

公司通常使用 Docker 或类似的容器作为其开发环境。这种方法确实有效,但大多数情况下,依赖项管理并不需要容器。语言包管理器可以处理简单的文件。

容器化增加了复杂性。它增加了一层额外的抽象,这可能会带来麻烦:配置、同步以及潜在的本地/CI 差异。

Docker 对于初学者来说可能具有挑战性,因为它需要额外的配置并且需要更多地了解基础设施。

就我个人而言,我不太喜欢使用容器进行开发。我一直都是直接在构建方案中全局获取并安装依赖项。

开发人员体验

  • 当实现一个新工具时,您通常不想改变现有的构建系统(GNU Make、CMake 等)。

  • 有时您无法控制要构建的包的存储库并且无法修改它。

Nix 尤其适合用作现有构建系统的外部包管理器。功能完善的构建系统清单对于“nix 化”项目来说是一个很大的优势。

IDE 集成

C/C++ 世界中的另一个痛点是 IDE 支持。

  • 默认情况下,编辑器不知道项目有哪些依赖项,并且只加载系统标题(例如/usr/include等)。

  • 如果手动构建库,则可能需要手动编码Include path

我想说,即使支持控制台构建,Plus 版本也存在问题。JavaScript 或 Rust 项目始终可以使用相同的命令构建,该命令将安装所需的编译器版本和库,运行其他配置脚本,并在需要时设置环境变量。在 Plus 版本中,我们可能有也可能没有带有build.sh自身依赖项的脚本。

/usr/include当您处理需要不同版本库的多个项目时,基于标头的脚本也很容易中断。

今天我们将用 Nix 解决所有这些问题。


目标

  1. 用 C 和 C++ 创建两个项目:使用用于处理 JSON 的库 -cJSON用于 C 和nlohmann_json用于 C++。

  2. 除了 Nix 之外,还可以使用 CMake 作为构建系统。

  3. 所有依赖项和工作环境均在存储库中描述:只需要 Nix 即可复制。

  4. 使用可用的 IntelliSense 和 CMake 插件在 VSCode 中开发。

  5. 易于配置、最大程度自动化和可重复性。

词汇表

  • 工作区是存储库的根,即您在 IDE(例如 VSCode)中打开的内容。

  • 项目/包是存储库的一部分,可以构建以生成有用的、独立的工件(例如,可执行文件)。


必要的

  • 启用薄片的 Nix 。

  • 我建议使用这个安装程序:

curl -fsSL https://install.determinate.systems/nix | sh -s -- install

Nix 可以通过多种方式安装,包括在系统中无需 root 权限的方式安装。

如果以下命令运行没有错误,则一切正常:

❯ nix flake --version  
nix (Nix) 2.28.3

本文基于Nix 2.28版本。

direnv

direnv 是一个命令行环境管理实用程序。虽然它与 Nix 没有直接关系,但经常与 Nix 结合使用。

现在您有了 Nix,您可以从 nixpkgs 安装程序:

nix profile install nixpkgs
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc

Nix 还具有支持语言服务器协议的 VSCode 扩展。为了简单起见,我们不会在本文中介绍它们。

存储库结构

.
├── CMakeLists.txt
├── README.md
├── flake.lock
├── flake.nix
└── src├── c│   ├── CMakeLists.txt│   └── main.c└── cc├── CMakeLists.txt└── main.cc

根CMakeLists.txt包含:

cmake_minimum_required(VERSION 3.10)
project(hello_json)add_subdirectory(src/c)
add_subdirectory(src/cc)

C 项目:hello-json-c

让我们从描述符开始CMakeLists.txt

find_package(cJSON REQUIRED)
add_executable(hello-json-c main.c)
target_link_libraries(hello-json-c PRIVATE cjson)
install(TARGETS hello-json-c DESTINATION bin)

尽管find_package()它在“系统”中搜索包,但当您或 Nix 运行构建命令时,它将设置必要的搜索路径并且它将起作用。

Nix 和其他软件包管理器会根据安装阶段写入输出install目录的内容来确定最终软件包的内容。因此,我们需要描述此阶段。如果没有描述,通过 Nix 进行构建将无法进行。通过执行命令进行手动构建cmake --build始终有效。

结果的目录结构必须符合FHS,因此我们在 中写入/bin

main.c:

#include <stdio.h>
#include <stdlib.h>
#include <cjson/cJSON.h>int main(void)
{cJSON *root = cJSON_CreateObject();cJSON_AddStringToObject(root, "message", "Hello, world from C and cJSON!");char *json_str = cJSON_Print(root);printf("%s\n", json_str);cJSON_Delete(root);free(json_str);return EXIT_SUCCESS;
}

这里的一切都很简单:我们从具有字段message和文本的对象创建 JSON "Hello, world from C and cJSON!"

C++ 项目:hello-json-cc

CMakeLists.txt没什么不同:

set(CMAKE_CXX_STANDARD 17)find_package(nlohmann_json REQUIRED)
add_executable(hello-json-cpp main.cc)
target_link_libraries(hello-json-cpp PRIVATE nlohmann_json::nlohmann_json)
install(TARGETS hello-json-cpp DESTINATION bin)

main.cc:

#include <iostream>
#include <nlohmann/json.hpp>int main()
{nlohmann::json hello;hello["message"] = "Hello, world from C++ and nlohmann::json!";std::cout << hello.dump(4) << std::endl;assert(hello.count("message") == 1);return 0;
}

我们有源文件和 CMake 构建配方,现在让我们继续用 Nix 语言描述依赖关系。

flake.nix

这里我们将描述两个包:

hello-json-c = pkgs.stdenv.mkDerivation {pname = "hello-json-c";version = "0.1.0";src = ./src/c;nativeBuildInputs = [pkgs.cjsonpkgs.cmake];
};

我们将项目命名为 C 项目,指定和hello-json-c作为构建依赖项。我们指定源代码文件的路径。cjsoncmake./src/c

该软件包hello-json-cc仅在一个依赖项上有所不同:

hello-json-cc = pkgs.stdenv.mkDerivation {pname = "hello-json-cc";version = "0.1.0";src = ./src/cc;nativeBuildInputs = [pkgs.cmakepkgs.nlohmann_json];
};

此处的 mkDerivation 函数基于 GNU Make、CMake 等常见构建工具生成项目构建脚本。nixpkgs手册中更详细地描述了构建环境。为了减少样板代码,构建脚本会根据构建依赖项进行调整,在本例中,将使用 CMake 而不是 GNU Make。如果您包含pkgs.ninja,它也将自动使用。

我们还需要一个包含所有包依赖项的开发环境:

devShells.${system}.default = pkgs.mkShell {name = "hello-json";inputsFrom = [hello-json-chello-json-cc];
};

最终文件的全部内容flake.nix:

{description = "Parse JSON in C and C++";inputs = {nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";};outputs ={ self, nixpkgs }:letsystem = "x86_64-linux";pkgs = nixpkgs.legacyPackages.${system};hello-json-c = pkgs.stdenv.mkDerivation {pname = "hello-json-c";version = "0.1.0";src = ./src/c;nativeBuildInputs = [pkgs.cjsonpkgs.cmake];};hello-json-cc = pkgs.stdenv.mkDerivation {pname = "hello-json-cc";version = "0.1.0";src = ./src/cc;nativeBuildInputs = [pkgs.cmakepkgs.nlohmann_json];};in{packages.${system} = { inherit hello-json-c hello-json-cc; };devShells.${system}.default = pkgs.mkShell {name = "hello-json";inputsFrom = [hello-json-chello-json-cc];};};
}

这样做git commit是因为 Nix 会忽略未由 Git 管理的文件。

开发者 Shell

此时,我们的工作区应该已经启动并运行了。要打开开发者 shell,请输入:

nix develop

它还将cmake包含和 的CMAKE_INCLUDE_PATH路径。因此,以下命令应该可以成功完成:cjsonnlohmann_json

cmake -S . -B build
cmake --build build --target hello-json-c
cmake --build build --target hello-json-cc
❯ ./build/src/c/hello-json-c 
{"message":      "Hello, world from C and cJSON!"
}❯ ./build/src/cc/hello-json-cc
{"message": "Hello, world from C++ and nlohmann::json!"
}

您可以按 退出 shell Ctrl+D

direnv

您可以自动加载和卸载开发人员外壳。

创建文件.envrc

use flake

要启用存储库中的环境,请键入

direnv allow

在存储库目录中。现在,存储库中当前目录中的终端将始终拥有开发人员所需的工具。

Visual Studio 代码

允许 direnv 扩展加载配置文件就足够了:

并重新启动窗口:

现在 CMake 将看到编译器:

大会的目标如下:

如果打开源代码文件,则可以看到标头已加载:

此外,当转到定义时,cJSON_Print()将打开文件/nix/store

请注意,除了安装并启用 direnv 扩展之外,我们没有以任何方式配置 VSCode 与该项目配合使用。配置在存储库的 Nix 文件中描述,并且对所有开发人员都相同。克隆存储库后,一切都会立即生效。

通过 Nix 构建和运行

我们已经描述了两个软件包。如果您不开发它们,您可能只需要它们的预构建版本,并且对手动构建过程不感兴趣。在这种情况下,最好使用 Nix 本身进行构建和运行:

❯ nix run .#hello-json-c
{"message":      "Hello, world from C and cJSON!"
}❯ nix run .#hello-json-cc
{"message": "Hello, world from C++ and nlohmann::json!"
}

事实上,你甚至不需要克隆仓库来构建和运行它!下面这个方法就可以了:

❯ nix run git+https://gitverse.ru/nix-store/hello-json#hello-json-c  
{  
"message": "Hello, world from C and cJSON!"  
}  ❯ nix run git+https://gitverse.ru/nix-store/hello-json#hello-json-cc  
{  
"message": "Hello, world from C++ and nlohmann::json!"  
}

结果

我们创建了一个包含两个项目的 monorepo:hello-json-chello-json-cc。我们使用 CMake 描述了它们的构建,以确保 Nix 体验与传统 Nix 一致。依赖项在中描述flake.nix,具体版本在中描述flake.lock。它们以相同的方式解析,并用于最终构建、CI、开发人员 shell 和 IDE。

我们的 IDE 开箱即用,功能齐全;您只需要克隆存储库并启用 direnv。

理想情况下,Nix 对于普通开发者来说是完全不可见的。Nix 仅用于管理依赖项,确保 CMake 和 IDE 始终能够在环境中找到所需的内容。

http://www.dtcms.com/a/553558.html

相关文章:

  • Kimi Linear 论文阅读笔记:第一次“线性注意力”全面胜过全注意力
  • 金华网站开发开发网站需要什么硬件
  • 使用mysql客户端工具造数据方法入门
  • 光刻胶分类与特性——g/i线光刻胶及东京应化TP-3000系列胶典型配方(下)
  • Spring Boot项目快速稳健架构指南
  • 网站wordpress错误网站设计宽度尺寸
  • 图像分割技术总结
  • iptables u32 match 对字节后退的支持
  • gymnasium中space用法
  • 【win11】4:funasr配置python依赖项并下载模型
  • 网站官网上的新闻列表怎么做官网排名优化方案
  • Flink 优化-状态及 Checkpoint 调优
  • 会员中心网站模板网站优化要素
  • 微软简化Windows更新!命名更加直观:只保留重点
  • 7.进程控制(三)
  • MSA 基因序列对比差异化 相关使用
  • Kafka(文件)数据存储、清理机制、高性能设计
  • 湖南免费网站建设怀化网站建设有哪些
  • Redis 从基础到实战
  • 投标建设用地是哪个网站广州seo培训
  • 做电影网站需要注意什么软件网站开发 erp系统开发
  • 删除wps的空白页
  • 计算机网络 —— F / 应用层
  • 62 VueComponent watcher 的实现
  • Ethernaut Level 15: Naught Coin - ERC20 approve/transferFrom漏洞
  • PySide6 实现win10 手动与自动切换主题 借助系统托盘
  • 上传项目至Github与从Github克隆项目
  • 做个人网站的步骤上海建筑设计公司平台
  • 如何使用一个模型完成多种交通任务?请看此文
  • 第N2周:构建词典