From 73f561c4265a0d1c32ec28bba7fb88526b409b06 Mon Sep 17 00:00:00 2001 From: Marc Jakobi Date: Tue, 10 Dec 2024 20:46:13 +0100 Subject: [PATCH] feat!: luarocks build backend --- .../sample-project-busted/project.rockspec | 2 +- .../foo-1.0.0-1.rockspec | 6 + rocks-lib/src/build/luarocks.rs | 86 +++++++ rocks-lib/src/build/mod.rs | 21 +- rocks-lib/src/build/utils.rs | 18 ++ rocks-lib/src/config/mod.rs | 40 +++- rocks-lib/src/lib.rs | 1 + rocks-lib/src/luarocks_installation.rs | 221 ++++++++++++++++++ rocks-lib/src/operations/install.rs | 27 ++- rocks-lib/src/rockspec/mod.rs | 5 +- rocks-lib/tests/luarocks_installation.rs | 45 ++++ 11 files changed, 447 insertions(+), 25 deletions(-) create mode 100644 rocks-lib/resources/test/sample-project-no-build-spec/foo-1.0.0-1.rockspec create mode 100644 rocks-lib/src/build/luarocks.rs create mode 100644 rocks-lib/src/luarocks_installation.rs create mode 100644 rocks-lib/tests/luarocks_installation.rs diff --git a/rocks-lib/resources/test/sample-project-busted/project.rockspec b/rocks-lib/resources/test/sample-project-busted/project.rockspec index dd16e00f..9d53212a 100644 --- a/rocks-lib/resources/test/sample-project-busted/project.rockspec +++ b/rocks-lib/resources/test/sample-project-busted/project.rockspec @@ -1,6 +1,6 @@ local _MODREV, _SPECREV = 'scm', '-1' rockspec_format = '3.0' -package = 'sample-project-busted' +package = 'sample-project' version = _MODREV .. _SPECREV test_dependencies = { diff --git a/rocks-lib/resources/test/sample-project-no-build-spec/foo-1.0.0-1.rockspec b/rocks-lib/resources/test/sample-project-no-build-spec/foo-1.0.0-1.rockspec new file mode 100644 index 00000000..b2f13aed --- /dev/null +++ b/rocks-lib/resources/test/sample-project-no-build-spec/foo-1.0.0-1.rockspec @@ -0,0 +1,6 @@ +rockspec_format = "3.0" +package = "foo" +version = "1.0.0-1" +source = { + url = 'https://github.com/nvim-neorocks/luarocks-stub', +} diff --git a/rocks-lib/src/build/luarocks.rs b/rocks-lib/src/build/luarocks.rs new file mode 100644 index 00000000..fd03e3bd --- /dev/null +++ b/rocks-lib/src/build/luarocks.rs @@ -0,0 +1,86 @@ +use std::{io, path::Path}; + +use crate::{ + config::Config, + lua_installation::LuaInstallation, + luarocks_installation::{ExecLuaRocksError, LuaRocksError, LuaRocksInstallation}, + progress::{Progress, ProgressBar}, + rockspec::Rockspec, + tree::RockLayout, +}; + +use tempdir::TempDir; +use thiserror::Error; + +use super::utils::recursive_copy_dir; + +#[derive(Error, Debug)] +pub enum LuarocksBuildError { + #[error(transparent)] + Io(#[from] io::Error), + #[error("error instantiating luarocks compatibility layer: {0}")] + LuaRocksError(#[from] LuaRocksError), + #[error("error running 'luarocks make': {0}")] + ExecLuaRocksError(#[from] ExecLuaRocksError), +} + +pub(crate) async fn build( + rockspec: &Rockspec, + output_paths: &RockLayout, + lua: &LuaInstallation, + config: &Config, + build_dir: &Path, + progress: &Progress, +) -> Result<(), LuarocksBuildError> { + progress.map(|p| { + p.set_message(format!( + "Building {} {} with luarocks...", + rockspec.package, rockspec.version + )) + }); + let rockspec_temp_dir = TempDir::new("temp-rockspec")?.into_path(); + let rockspec_file = rockspec_temp_dir.join(format!( + "{}-{}.rockspec", + rockspec.package, rockspec.version + )); + std::fs::write(&rockspec_file, &rockspec.raw_content)?; + let luarocks = LuaRocksInstallation::new(config)?; + let luarocks_tree = TempDir::new("luarocks-compat-tree")?; + luarocks.make(&rockspec_file, build_dir, luarocks_tree.path(), lua)?; + install(rockspec, &luarocks_tree.into_path(), output_paths, config) +} + +fn install( + rockspec: &Rockspec, + luarocks_tree: &Path, + output_paths: &RockLayout, + config: &Config, +) -> Result<(), LuarocksBuildError> { + let lua_version = rockspec + .lua_version_from_config(config) + .expect("could not get lua version!"); + std::fs::create_dir_all(&output_paths.bin)?; + let package_dir = luarocks_tree + .join("lib") + .join("lib") + .join("luarocks") + .join(format!( + "rocks-{}", + &lua_version.version_compatibility_str() + )) + .join(format!("{}", rockspec.package)) + .join(format!("{}", rockspec.version)); + recursive_copy_dir(&package_dir.join("doc"), &output_paths.doc)?; + recursive_copy_dir(&luarocks_tree.join("bin"), &output_paths.bin)?; + let src_dir = luarocks_tree + .join("share") + .join("lua") + .join(lua_version.version_compatibility_str()); + recursive_copy_dir(&src_dir, &output_paths.src)?; + let lib_dir = luarocks_tree + .join("lib") + .join("lua") + .join(lua_version.version_compatibility_str()); + recursive_copy_dir(&lib_dir, &output_paths.lib)?; + Ok(()) +} diff --git a/rocks-lib/src/build/mod.rs b/rocks-lib/src/build/mod.rs index 319a007d..a39ce38b 100644 --- a/rocks-lib/src/build/mod.rs +++ b/rocks-lib/src/build/mod.rs @@ -13,10 +13,13 @@ use crate::{ }; pub(crate) mod utils; // Make build utilities available as a submodule use indicatif::style::TemplateError; +use luarocks::LuarocksBuildError; use make::MakeError; use rust_mlua::RustError; use thiserror::Error; +use utils::recursive_copy_dir; mod builtin; +mod luarocks; mod make; mod rust_mlua; pub mod variables; @@ -43,6 +46,8 @@ pub enum BuildError { stdout: String, stderr: String, }, + #[error(transparent)] + LuarocksBuildError(#[from] LuarocksBuildError), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -87,6 +92,9 @@ async fn run_build( .run(output_paths, false, lua, config, build_dir, progress) .await? } + Some(BuildBackendSpec::LuaRock(_)) => { + luarocks::build(rockspec, output_paths, lua, config, build_dir, progress).await?; + } None => (), _ => unimplemented!(), } @@ -214,19 +222,8 @@ pub async fn build( install(&rockspec, &tree, &output_paths, &lua, &build_dir, progress).await?; - // Copy over all `copy_directories` to their respective paths for directory in &rockspec.build.current_platform().copy_directories { - for file in walkdir::WalkDir::new(build_dir.join(directory)) - .into_iter() - .flatten() - { - if file.file_type().is_file() { - let filepath = file.path(); - let target = output_paths.etc.join(filepath); - std::fs::create_dir_all(target.parent().unwrap())?; - std::fs::copy(filepath, target)?; - } - } + recursive_copy_dir(&build_dir.join(directory), &output_paths.etc)?; } Ok(package) diff --git a/rocks-lib/src/build/utils.rs b/rocks-lib/src/build/utils.rs index e8c6e27e..aea1f5ef 100644 --- a/rocks-lib/src/build/utils.rs +++ b/rocks-lib/src/build/utils.rs @@ -27,6 +27,24 @@ pub(crate) fn copy_lua_to_module_path( Ok(()) } +pub(crate) fn recursive_copy_dir(src: &PathBuf, dest: &Path) -> Result<(), io::Error> { + if src.exists() { + for file in walkdir::WalkDir::new(src) + .into_iter() + .flatten() + .filter(|file| file.file_type().is_file()) + { + let relative_src_path: PathBuf = + pathdiff::diff_paths(src.join(file.clone().into_path()), src) + .expect("failed to copy directories!"); + let filepath = file.path(); + let target = dest.join(relative_src_path); + std::fs::create_dir_all(target.parent().unwrap())?; + std::fs::copy(filepath, target)?; + } + } + Ok(()) +} fn validate_output(output: Output) -> Result<(), BuildError> { if !output.status.success() { return Err(BuildError::CommandFailure { diff --git a/rocks-lib/src/config/mod.rs b/rocks-lib/src/config/mod.rs index 8a9bb724..80f9b441 100644 --- a/rocks-lib/src/config/mod.rs +++ b/rocks-lib/src/config/mod.rs @@ -37,14 +37,19 @@ impl LuaVersion { LuaVersion::LuaJIT52 => "5.2.0".parse().unwrap(), } } - pub fn as_version_req(&self) -> PackageVersionReq { + pub fn version_compatibility_str(&self) -> String { match self { - LuaVersion::Lua51 | LuaVersion::LuaJIT => "~> 5.1".parse().unwrap(), - LuaVersion::Lua52 | LuaVersion::LuaJIT52 => "~> 5.2".parse().unwrap(), - LuaVersion::Lua53 => "~> 5.3".parse().unwrap(), - LuaVersion::Lua54 => "~> 5.4".parse().unwrap(), + LuaVersion::Lua51 | LuaVersion::LuaJIT => "5.1".into(), + LuaVersion::Lua52 | LuaVersion::LuaJIT52 => "5.2".into(), + LuaVersion::Lua53 => "5.3".into(), + LuaVersion::Lua54 => "5.4".into(), } } + pub fn as_version_req(&self) -> PackageVersionReq { + format!("~> {}", self.version_compatibility_str()) + .parse() + .unwrap() + } /// Get the LuaVersion from a version that has been parsed from the `lua -v` output pub fn from_version(version: PackageVersion) -> Result { @@ -139,6 +144,7 @@ pub struct Config { lua_dir: PathBuf, lua_version: Option, tree: PathBuf, + luarocks_tree: PathBuf, no_project: bool, verbose: bool, timeout: Duration, @@ -170,6 +176,10 @@ impl Config { ..self } } + + pub fn with_tree(self, tree: PathBuf) -> Self { + Self { tree, ..self } + } } impl Config { @@ -206,6 +216,11 @@ impl Config { &self.tree } + /// The tree in which to install luarocks for use as a compatibility layer + pub fn luarocks_tree(&self) -> &PathBuf { + &self.luarocks_tree + } + pub fn no_project(&self) -> bool { self.no_project } @@ -259,6 +274,7 @@ pub struct ConfigBuilder { lua_dir: Option, lua_version: Option, tree: Option, + luarocks_tree: Option, no_project: Option, verbose: Option, timeout: Option, @@ -319,6 +335,13 @@ impl ConfigBuilder { Self { tree, ..self } } + pub fn luarocks_tree(self, luarocks_tree: Option) -> Self { + Self { + luarocks_tree, + ..self + } + } + pub fn no_project(self, no_project: Option) -> Self { Self { no_project, ..self } } @@ -349,6 +372,7 @@ impl ConfigBuilder { pub fn build(self) -> Result { let data_dir = self.data_dir.unwrap_or(Config::get_default_data_path()?); + let cache_dir = self.cache_dir.unwrap_or(Config::get_default_cache_path()?); let current_project = Project::current()?; let lua_version = self .lua_version @@ -358,7 +382,6 @@ impl ConfigBuilder { .or(crate::lua_installation::get_installed_lua_version("lua") .ok() .and_then(|version| LuaVersion::from_version(version).ok())); - Ok(Config { enable_development_rockspecs: self.enable_development_rockspecs.unwrap_or(false), server: self @@ -380,13 +403,14 @@ impl ConfigBuilder { .map(|project| project.root().join(".rocks")) } }) - .unwrap_or_else(|| data_dir.clone()), + .unwrap_or_else(|| data_dir.join("tree")), + luarocks_tree: self.luarocks_tree.unwrap_or(data_dir.join(".luarocks")), no_project: self.no_project.unwrap_or(false), verbose: self.verbose.unwrap_or(false), timeout: self.timeout.unwrap_or_else(|| Duration::from_secs(30)), make: self.make.unwrap_or("make".into()), variables: self.variables.unwrap_or_default(), - cache_dir: self.cache_dir.unwrap_or(Config::get_default_cache_path()?), + cache_dir, data_dir, }) } diff --git a/rocks-lib/src/lib.rs b/rocks-lib/src/lib.rs index 6933b652..bff49f01 100644 --- a/rocks-lib/src/lib.rs +++ b/rocks-lib/src/lib.rs @@ -3,6 +3,7 @@ pub mod config; pub mod hash; pub mod lockfile; pub mod lua_installation; +pub mod luarocks_installation; pub mod manifest; pub mod operations; pub mod package; diff --git a/rocks-lib/src/luarocks_installation.rs b/rocks-lib/src/luarocks_installation.rs new file mode 100644 index 00000000..8763cafc --- /dev/null +++ b/rocks-lib/src/luarocks_installation.rs @@ -0,0 +1,221 @@ +use itertools::Itertools; +use std::{ + io, + path::Path, + process::{Command, ExitStatus}, +}; +use tempdir::TempDir; +use thiserror::Error; + +use crate::{ + build::{build, BuildBehaviour, BuildError}, + config::{Config, LuaVersion, LuaVersionUnset}, + lockfile::{LockConstraint, PinnedState}, + lua_installation::LuaInstallation, + manifest::{ManifestError, ManifestMetadata}, + operations::download_rockspec, + package::PackageReq, + path::Paths, + progress::{Progress, ProgressBar}, + rockspec::{Rockspec, RockspecFormat}, + tree::Tree, +}; + +#[derive(Error, Debug)] +pub enum LuaRocksError { + #[error(transparent)] + LuaVersionUnset(#[from] LuaVersionUnset), + #[error(transparent)] + Io(#[from] io::Error), +} + +#[derive(Error, Debug)] +pub enum LuaRocksInstallError { + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] + ManifestError(#[from] ManifestError), + #[error(transparent)] + BuildError(#[from] BuildError), +} + +#[derive(Error, Debug)] +pub enum InstallBuildDependenciesError { + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] + ManifestError(#[from] ManifestError), + #[error(transparent)] + BuildError(#[from] BuildError), +} + +#[derive(Error, Debug)] +pub enum ExecLuaRocksError { + #[error(transparent)] + LuaVersionUnset(#[from] LuaVersionUnset), + #[error("could not write luarocks config: {0}")] + WriteLuarocksConfigError(io::Error), + #[error("failed to run luarocks: {0}")] + Io(#[from] io::Error), + #[error("executing luarocks compatibility layer failed.\nstatus: {status}\nstdout: {stdout}\nstderr: {stderr}")] + CommandFailure { + status: ExitStatus, + stdout: String, + stderr: String, + }, +} + +pub struct LuaRocksInstallation { + pub tree: Tree, + pub config: Config, +} + +const LUAROCKS_VERSION: &str = "3.11.1-1"; + +const LUAROCKS_ROCKSPEC: &str = " +rockspec_format = '3.0' +package = 'luarocks' +version = '3.11.1-1' +source = { + url = 'git+https://github.com/luarocks/luarocks', + tag = 'v3.11.1' +} +"; + +impl LuaRocksInstallation { + pub fn new(config: &Config) -> Result { + let config = config.clone().with_tree(config.luarocks_tree().clone()); + let luarocks_installation = Self { + tree: Tree::new(config.luarocks_tree().clone(), LuaVersion::from(&config)?)?, + config, + }; + Ok(luarocks_installation) + } + + pub async fn ensure_installed( + self: &LuaRocksInstallation, + progress: &Progress, + ) -> Result<(), LuaRocksInstallError> { + let mut lockfile = self.tree.lockfile()?; + let luarocks_req = + PackageReq::new("luarocks".into(), Some(LUAROCKS_VERSION.into())).unwrap(); + if self.tree.has_rock(&luarocks_req).is_none() { + let rockspec = Rockspec::new(LUAROCKS_ROCKSPEC).unwrap(); + let pkg = build( + rockspec, + PinnedState::Unpinned, + LockConstraint::Constrained(luarocks_req.version_req().clone()), + BuildBehaviour::NoForce, + &self.config, + progress, + ) + .await?; + lockfile.add(&pkg); + } + lockfile.flush()?; + Ok(()) + } + + pub async fn install_build_dependencies( + self, + rockspec: &Rockspec, + progress: &Progress, + ) -> Result<(), InstallBuildDependenciesError> { + let mut lockfile = self.tree.lockfile()?; + let manifest = ManifestMetadata::from_config(&self.config).await?; + let build_dependencies = match rockspec.rockspec_format { + Some(RockspecFormat::_1_0 | RockspecFormat::_2_0) => { + // XXX: These formats don't support `build_dependencies`. + rockspec.dependencies.current_platform() + } + _ => rockspec.build_dependencies.current_platform(), + } + .iter() + .collect_vec(); + for package in build_dependencies { + if self.tree.has_rock(package).is_none() { + let rockspec = download_rockspec(package, &manifest, &self.config, progress) + .await + .unwrap(); + let pkg = build( + rockspec, + PinnedState::Unpinned, + LockConstraint::Constrained(package.version_req().clone()), + BuildBehaviour::NoForce, + &self.config, + progress, + ) + .await?; + lockfile.add(&pkg); + } + } + lockfile.flush()?; + Ok(()) + } + + pub fn make( + self, + rockspec_path: &Path, + build_dir: &Path, + dest_dir: &Path, + lua: &LuaInstallation, + ) -> Result<(), ExecLuaRocksError> { + let dest_dir_str = dest_dir.to_string_lossy().to_string(); + let rockspec_path_str = rockspec_path.to_string_lossy().to_string(); + let args = vec![ + "make", + "--deps-mode", + "none", + "--tree", + &dest_dir_str, + &rockspec_path_str, + ]; + self.exec(args, build_dir, lua) + } + + fn exec( + self, + args: Vec<&str>, + cwd: &Path, + lua: &LuaInstallation, + ) -> Result<(), ExecLuaRocksError> { + let luarocks_paths = Paths::from_tree(self.tree)?; + // Ensure a pure environment so we can do parallel builds + let temp_dir = TempDir::new("rocks-run-luarocks").unwrap(); + let luarocks_config_content = format!( + " +variables = {{ + LUA_LIBDIR = \"{0}\", + LUA_INCDIR = \"{1}\", + LUA_VERSION = \"{2}\", + MAKE = \"{3}\", +}} +", + lua.lib_dir.display(), + lua.include_dir.display(), + LuaVersion::from(&self.config)?, + self.config.make_cmd(), + ); + let luarocks_config = temp_dir.path().join("luarocks-config.lua"); + std::fs::write(luarocks_config.clone(), luarocks_config_content) + .map_err(ExecLuaRocksError::WriteLuarocksConfigError)?; + let output = Command::new("luarocks") + .current_dir(cwd) + .args(args) + .env("PATH", luarocks_paths.path_appended().joined()) + .env("LUA_PATH", luarocks_paths.package_path().joined()) + .env("LUA_CPATH", luarocks_paths.package_cpath().joined()) + .env("HOME", temp_dir.into_path()) + .env("LUAROCKS_CONFIG", luarocks_config) + .output()?; + if output.status.success() { + Ok(()) + } else { + Err(ExecLuaRocksError::CommandFailure { + status: output.status, + stdout: String::from_utf8_lossy(&output.stdout).into(), + stderr: String::from_utf8_lossy(&output.stderr).into(), + }) + } + } +} diff --git a/rocks-lib/src/operations/install.rs b/rocks-lib/src/operations/install.rs index 233cb121..48f282c2 100644 --- a/rocks-lib/src/operations/install.rs +++ b/rocks-lib/src/operations/install.rs @@ -6,11 +6,14 @@ use crate::{ lockfile::{ LocalPackage, LocalPackageId, LocalPackageSpec, LockConstraint, Lockfile, PinnedState, }, - manifest::ManifestMetadata, + luarocks_installation::{ + InstallBuildDependenciesError, LuaRocksError, LuaRocksInstallError, LuaRocksInstallation, + }, + manifest::{ManifestError, ManifestMetadata}, operations::download_rockspec, package::{PackageName, PackageReq, PackageVersionReq}, progress::{MultiProgress, Progress, ProgressBar}, - rockspec::{LuaVersionError, Rockspec}, + rockspec::{BuildBackendSpec, LuaVersionError, Rockspec}, tree::Tree, }; @@ -33,6 +36,14 @@ pub enum InstallError { LuaVersionUnset(#[from] LuaVersionUnset), #[error(transparent)] Io(#[from] io::Error), + #[error("error instantiating LuaRocks compatibility layer: {0}")] + LuaRocksError(#[from] LuaRocksError), + #[error("error installing LuaRocks compatibility layer: {0}")] + LuaRocksInstallError(#[from] LuaRocksInstallError), + #[error("error installing LuaRocks build dependencies: {0}")] + InstallBuildDependenciesError(#[from] InstallBuildDependenciesError), + #[error(transparent)] + ManifestError(#[from] ManifestError), #[error("failed to build {0}: {1}")] BuildError(PackageName, BuildError), } @@ -89,6 +100,7 @@ async fn install_impl( let installed_packages = join_all(all_packages.clone().into_values().map(|install_spec| { let package = install_spec.rockspec.package.clone(); + let bar = progress.map(|p| { p.add(ProgressBar::from(format!( "💻 Installing {}", @@ -98,8 +110,17 @@ async fn install_impl( let config = config.clone(); tokio::spawn(async move { + let rockspec = install_spec.rockspec; + if let Some(BuildBackendSpec::LuaRock(_)) = + rockspec.build.current_platform().build_backend + { + let luarocks = LuaRocksInstallation::new(&config)?; + luarocks.ensure_installed(&bar).await?; + luarocks.install_build_dependencies(&rockspec, &bar).await?; + } + let pkg = crate::build::build( - install_spec.rockspec, + rockspec, pin, install_spec.spec.constraint(), install_spec.build_behaviour, diff --git a/rocks-lib/src/rockspec/mod.rs b/rocks-lib/src/rockspec/mod.rs index a3016ec8..b7ac3150 100644 --- a/rocks-lib/src/rockspec/mod.rs +++ b/rocks-lib/src/rockspec/mod.rs @@ -53,12 +53,14 @@ pub struct Rockspec { pub source: PerPlatform, pub build: PerPlatform, pub test: PerPlatform, + /// The original content of this rockspec, needed by luarocks + pub raw_content: String, /// The sha256 of this rockspec hash: Integrity, } impl Rockspec { - pub fn new(rockspec_content: &String) -> Result { + pub fn new(rockspec_content: &str) -> Result { let lua = Lua::new(); lua.load(rockspec_content).exec()?; @@ -77,6 +79,7 @@ impl Rockspec { build: globals.get("build")?, test: globals.get("test")?, hash: Integrity::from(rockspec_content), + raw_content: rockspec_content.into(), }; let rockspec_file_name = format!("{}-{}.rockspec", rockspec.package, rockspec.version); diff --git a/rocks-lib/tests/luarocks_installation.rs b/rocks-lib/tests/luarocks_installation.rs new file mode 100644 index 00000000..646c42f3 --- /dev/null +++ b/rocks-lib/tests/luarocks_installation.rs @@ -0,0 +1,45 @@ +use std::path::PathBuf; + +use assert_fs::assert::PathAssert; +use assert_fs::prelude::{PathChild as _, PathCopy}; +use assert_fs::TempDir; +use predicates::prelude::predicate; +use rocks_lib::progress::{MultiProgress, Progress, ProgressBar}; +use rocks_lib::{ + config::{ConfigBuilder, LuaVersion}, + lua_installation::LuaInstallation, + luarocks_installation::LuaRocksInstallation, +}; + +#[tokio::test] +async fn luarocks_make() { + let dir = TempDir::new().unwrap(); + let config = ConfigBuilder::new() + .tree(Some(dir.path().into())) + .build() + .unwrap(); + let luarocks = LuaRocksInstallation::new(&config).unwrap(); + let progress = Progress::Progress(MultiProgress::new()); + let bar = progress.map(|p| p.add(ProgressBar::from("Installing luarocks".to_string()))); + luarocks.ensure_installed(&bar).await.unwrap(); + let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("resources/test/sample-project-no-build-spec"); + let rockspec_path = project_root.join("foo-1.0.0-1.rockspec"); + let build_dir = TempDir::new().unwrap(); + build_dir.copy_from(&project_root, &["**"]).unwrap(); + let dest_dir = TempDir::new().unwrap(); + let lua_version = config.lua_version().unwrap_or(&LuaVersion::Lua51); + let lua = LuaInstallation::new(lua_version, &config); + luarocks + .make(&rockspec_path, build_dir.path(), dest_dir.path(), &lua) + .unwrap(); + let foo_dir = dest_dir + .child("share") + .child("lua") + .child(lua_version.version_compatibility_str()) + .child("foo"); + foo_dir.assert(predicate::path::is_dir()); + let foo_init = foo_dir.child("init.lua"); + foo_init.assert(predicate::path::is_file()); + foo_init.assert(predicate::str::contains("return true")); +}