Skip to content

Adding ability to generate nuget packages #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ repository = "https://github.com/asg017/sqlite-dist"


[dependencies]
base16ct = {version="0.2.0", features=["alloc"]}
base16ct = { version = "0.2.0", features = ["alloc"] }
base64 = "0.21.7"
chrono = "0.4.34"
clap = "4.5.1"
flate2 = "1.0.28"
semver = {version="1.0.22", features = ["serde"]}
serde = {version="1.0", features = ["derive"]}
semver = { version = "1.0.22", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.10.8"
tar = "0.4.40"
thiserror = "1.0.57"
toml = "0.8.10"
ureq = "2.9.6"
zip = "0.6.6"
serde-xml-rs = "0.5.1"

[profile.dist]
inherits = "release"
Expand Down
11 changes: 11 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod gh_releases;
mod installer_sh;
mod manifest;
mod npm;
mod nuget;
mod pip;
mod spec;
mod spm;
Expand Down Expand Up @@ -138,6 +139,7 @@ enum GeneratedAssetKind {
Spm,
Amalgamation,
Manifest,
Nuget,
}

impl ToString for GeneratedAssetKind {
Expand All @@ -154,6 +156,7 @@ impl ToString for GeneratedAssetKind {
GeneratedAssetKind::Spm => "spm".to_owned(),
GeneratedAssetKind::Amalgamation => "amalgamation".to_owned(),
GeneratedAssetKind::Manifest => "sqlite-dist-manifest".to_owned(),
GeneratedAssetKind::Nuget => "nuget".to_owned(),
}
}
}
Expand Down Expand Up @@ -541,6 +544,14 @@ fn build(matches: ArgMatches) -> Result<(), BuildError> {
std::fs::create_dir(&gem_path)?;
generated_assets.extend(gem::write_gems(&project, &gem_path, gem_config)?);
};
if project.spec.targets.nuget.is_some() {
let nuget_output_directory = output_dir.join("nuget");
std::fs::create_dir(&nuget_output_directory)?;
generated_assets.extend(nuget::write_nuget_packages(
&project,
&nuget_output_directory,
)?);
}

let github_releases_checksums_txt = generated_assets
.iter()
Expand Down
230 changes: 230 additions & 0 deletions src/nuget.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
use crate::{Cpu, GeneratedAsset, GeneratedAssetKind, Os, Project};
use serde::{Deserialize, Serialize};
use std::{
io::{self, Cursor, Write},
path::Path,
};
use zip::{write::FileOptions, ZipWriter};

#[derive(Debug, Deserialize, Serialize)]
pub struct Nuspec {
pub metadata: Metadata,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Metadata {
pub id: String,
pub version: String,
pub title: String,
pub authors: String,
pub owners: String,
pub require_license_acceptance: bool,
pub description: String,
}

impl Nuspec {
fn new(project: &Project) -> Self {
let author = project.spec.package.authors.first().unwrap();
Self {
metadata: Metadata {
id: project.spec.package.name.clone(),
version: project.version.to_string(),
title: project.spec.package.name.clone(),
authors: author.clone(),
owners: author.clone(),
require_license_acceptance: false,
description: project.spec.package.description.clone(),
},
}
}

fn to_xml(&self) -> String {
format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>{}</id>
<version>{}</version>
<title>{}</title>
<authors>{}</authors>
<owners>{}</owners>
<requireLicenseAcceptance>{}</requireLicenseAcceptance>
<description>{}</description>
</metadata>
</package>"#,
self.metadata.id,
self.metadata.version,
self.metadata.title,
self.metadata.authors,
self.metadata.owners,
self.metadata.require_license_acceptance,
self.metadata.description
)
}
}

pub struct NugetPackage {
zip: ZipWriter<Cursor<Vec<u8>>>,
}

impl NugetPackage {
pub fn new(package_id: &str) -> io::Result<Self> {
let buffer = Vec::new();
let cursor = Cursor::new(buffer);
let mut zip = ZipWriter::new(cursor);
zip.start_file("[Content_Types].xml", FileOptions::default())?;
zip.write_all(Self::content_types().as_bytes())?;
// add props_file to the folder buildTransitive\net46
let props_file_name = format!("build/netstandard2.0/{}.props", package_id);
zip.start_file(&props_file_name, FileOptions::default())?;
zip.write_all(Self::props_file().as_bytes())?;

// add targets_file to the folder buildTransitive\net46
let targets_file_name = format!("build/netstandard2.0/{}.targets", package_id);
zip.start_file(&targets_file_name, FileOptions::default())?;
zip.write_all(Self::targets_file(package_id).as_bytes())?;
Ok(Self { zip })
}

fn content_types() -> String {
r#"<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Default Extension="nuspec" ContentType="application/octet-stream"/>
<Default Extension="psmdcp" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
</Types>"#.to_string()
}

fn props_file() -> String {
r#"<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<!--
NuGet packages.config doesn't support native assemblies automatically,
so copy the native assemblies to the output directory.
-->
<ItemGroup Condition="Exists('packages.config') OR
Exists('$(MSBuildProjectName).packages.config') OR
Exists('packages.$(MSBuildProjectName).config')">
<Content Include="$(MSBuildThisFileDirectory)\..\..\runtimes\win-x64\native\*.dll"
Condition="'$(PlatformTarget)' == 'x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
<Link>%(Filename)%(Extension)</Link>
</Content>

<Content Include="$(MSBuildThisFileDirectory)\..\..\runtimes\win-x86\native\*.dll"
Condition="'$(PlatformTarget)' == 'x86'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
<Link>%(Filename)%(Extension)</Link>
</Content>

<Content Include="$(MSBuildThisFileDirectory)\..\..\runtimes\win-arm64\native\*.dll"
Condition="'$(PlatformTarget)' == 'arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
<Link>%(Filename)%(Extension)</Link>
</Content>
</ItemGroup>

</Project>"#
.to_string()
}

fn targets_file(package_id: &str) -> String {
format!(
r#"<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<EnableUnsupportedPlatformTargetCheck Condition="'$(EnableUnsupportedPlatformTargetCheck)' == ''">true</EnableUnsupportedPlatformTargetCheck>
</PropertyGroup>

<Target Name="_CheckForUnsupportedPlatformTarget"
Condition="'$(EnableUnsupportedPlatformTargetCheck)' == 'true'"
AfterTargets="_CheckForInvalidConfigurationAndPlatform">

<!--
Special case .NET Core portable applications. When building a portable .NET Core app,
the PlatformTarget is empty, and you don't know until runtime (i.e. which dotnet.exe)
what processor architecture will be used.
-->
<Error Condition="('$(PlatformTarget)' != 'x64' AND '$(PlatformTarget)' != 'x86') AND
('$(OutputType)' == 'Exe' OR '$(OutputType)'=='WinExe') AND
!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(PlatformTarget)' == '')"
Text="{} currently supports 'x64' and 'x86' processor architectures. Please ensure your application is targeting 'x64' or 'x86'." />
</Target>

</Project>"#,
package_id
)
}

pub fn add_nuspec(&mut self, nuspec: &Nuspec) -> io::Result<()> {
let nuspec_file_name = format!("{}.nuspec", nuspec.metadata.id);
let nuspec_content = nuspec.to_xml();
self.zip
.start_file(&nuspec_file_name, FileOptions::default())?;
self.zip.write_all(nuspec_content.as_bytes())?;
Ok(())
}

pub fn add_files(&mut self, project: &Project) -> io::Result<()> {
for platform_dir in &project.platform_directories {
if !(matches!(platform_dir.os, Os::Linux | Os::Macos | Os::Windows)
&& matches!(platform_dir.cpu, Cpu::X86_64 | Cpu::Aarch64))
{
continue;
}
for loadable_file in &platform_dir.loadable_files {
let os = match platform_dir.os {
Os::Windows => "win",
Os::Linux => "linux",
Os::Macos => "osx",
Os::Android => "android",
Os::Ios => "ios",
Os::IosSimulator => "ios-simulator",
};
let cpu = match platform_dir.cpu {
Cpu::Aarch64 => "arm64",
Cpu::X86_64 => "x64",
Cpu::I686 => "x86",
Cpu::Armv7a => "armv7",
};
let file_path =
format!("runtimes/{}-{}/native/{}", os, cpu, loadable_file.file.name);
self.zip.start_file(&file_path, FileOptions::default())?;
self.zip.write_all(&loadable_file.file.data)?;
}
}
Ok(())
}

pub fn finish(mut self) -> io::Result<Vec<u8>> {
let result = self.zip.finish()?;
Ok(result.into_inner())
}
}

pub(crate) fn write_nuget_packages(
project: &Project,
nuget_output_directory: &Path,
) -> io::Result<Vec<GeneratedAsset>> {
let mut assets = vec![];
let nuspec = Nuspec::new(project);
let mut package = NugetPackage::new(&nuspec.metadata.id)?;
package.add_nuspec(&nuspec)?;
package.add_files(project)?;
let buffer = package.finish()?;
let output_path = nuget_output_directory.join(format!(
"{}.{}.nupkg",
nuspec.metadata.id, nuspec.metadata.version
));
std::fs::write(&output_path, &buffer)?;
assets.push(GeneratedAsset::from(
GeneratedAssetKind::Nuget,
&output_path,
&buffer,
)?);
Ok(assets)
}
4 changes: 4 additions & 0 deletions src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ pub struct TargetAmalgamation {
pub include: Vec<String>,
}

#[derive(Deserialize)]
pub struct TargetNuget {}

#[derive(Deserialize)]
pub struct Targets {
pub github_releases: Option<TargetGithubRelease>,
Expand All @@ -62,6 +65,7 @@ pub struct Targets {
pub npm: Option<TargetNpm>,
pub gem: Option<TargetGem>,
pub amalgamation: Option<TargetAmalgamation>,
pub nuget: Option<TargetNuget>,
}
#[derive(Deserialize)]
pub struct Spec {
Expand Down