From 7901b1ce7541202a6489957ee481aec488a0ba9a Mon Sep 17 00:00:00 2001 From: ektrah Date: Mon, 20 Feb 2017 10:34:42 +0100 Subject: [PATCH] Add scripts and files to package libsodium for .NET Core (#477) --- packaging/dotnet-core/.gitignore | 5 + packaging/dotnet-core/README.md | 48 +++++ packaging/dotnet-core/_service | 17 ++ packaging/dotnet-core/libsodium.nuspec | 19 ++ packaging/dotnet-core/prepare.py | 268 +++++++++++++++++++++++++ packaging/dotnet-core/version.json | 3 + 6 files changed, 360 insertions(+) create mode 100644 packaging/dotnet-core/.gitignore create mode 100644 packaging/dotnet-core/README.md create mode 100644 packaging/dotnet-core/_service create mode 100644 packaging/dotnet-core/libsodium.nuspec create mode 100644 packaging/dotnet-core/prepare.py create mode 100644 packaging/dotnet-core/version.json diff --git a/packaging/dotnet-core/.gitignore b/packaging/dotnet-core/.gitignore new file mode 100644 index 00000000..bf99e1ea --- /dev/null +++ b/packaging/dotnet-core/.gitignore @@ -0,0 +1,5 @@ +build +cache +libsodium.*.nupkg +libsodium.*.nuspec +Makefile diff --git a/packaging/dotnet-core/README.md b/packaging/dotnet-core/README.md new file mode 100644 index 00000000..370afdc4 --- /dev/null +++ b/packaging/dotnet-core/README.md @@ -0,0 +1,48 @@ +This directory contains scripts and files to package libsodium for .NET Core. + +In .NET Core, it is customary to provide pre-compiled binaries for all platforms +as NuGet packages. The purpose of the `prepare.py` script in this directory is +to generate a `Makefile` that downloads pre-compiled libsodium binaries from a +list of sources and creates a NuGet package that can be uploaded to +[nuget.org](https://nuget.org/). + + +#### Sources + +The list of sources is located in `prepare.py` and needs to be updated on each +libsodium release. Currently, libsodium binaries are obtained in the following +ways: + +* For Windows, the binaries are taken from + [download.libsodium.org](https://download.libsodium.org/libsodium/releases/). +* For macOS, the binaries are extracted from the + [Homebrew libsodium bottle](https://bintray.com/homebrew/bottles/libsodium). +* For Linux, libsodium is compiled on and downloaded from the + [openSUSE Build Service](https://build.opensuse.org/package/show/home:nsec/libsodium). + + +#### Metadata + +The metadata for the NuGet package is located in `libsodium.nuspec`. On each +invocation, the `prepare.py` script generates a new version number and creates a +`.nuspec` file that contains the metadata from `libsodium.nuspec`, the generated +version number and the list of binaries. + + +#### Making a release + +1. Update `_service` to reflect the current libsodium version. +2. Download `libsodium.spec` and `libsodium.changes` from + [here](https://build.opensuse.org/package/show?project=devel%3Alibraries%3Ac_c%2B%2B&package=libsodium). +3. Upload `_service`, `libsodium.spec`, `libsodium.changes` and the current + `libsodium-{version}.tar.gz` to openSUSE Build Service. +4. Update the libsodium version, file names and download URLs in `prepare.py`. +5. Run `./prepare.py` to generate the `Makefile` and + `libsodium.{version}.nuspec`. +6. Run `make` to download the binaries and create `libsodium.{version}.nupkg`. + You may need to install `unzip`, `rpm2cpio` and `dotnet-dev` first. +7. Verify that everything in the `.nupkg` file is in place. +8. Publish the release by uploading the `.nupkg` file to + [nuget.org](https://nuget.org/). +9. Commit the updated `prepare.py` and `version.json` to the libsodium + repository. diff --git a/packaging/dotnet-core/_service b/packaging/dotnet-core/_service new file mode 100644 index 00000000..c6895234 --- /dev/null +++ b/packaging/dotnet-core/_service @@ -0,0 +1,17 @@ + + + ftp + ftp.us.debian.org + /debian/pool/main/libs/libsodium/libsodium_1.0.11-1.dsc + + + ftp + ftp.us.debian.org + /debian/pool/main/libs/libsodium/libsodium_1.0.11.orig.tar.gz + + + ftp + ftp.us.debian.org + /debian/pool/main/libs/libsodium/libsodium_1.0.11-1.debian.tar.xz + + diff --git a/packaging/dotnet-core/libsodium.nuspec b/packaging/dotnet-core/libsodium.nuspec new file mode 100644 index 00000000..12a254a9 --- /dev/null +++ b/packaging/dotnet-core/libsodium.nuspec @@ -0,0 +1,19 @@ + + + + libsodium + + Frank Denis + Frank Denis + true + https://raw.githubusercontent.com/jedisct1/libsodium/master/LICENSE + https://libsodium.org/ + + A modern and easy-to-use crypto library, packaged for use with .NET Core + © 2013-2017 Frank Denis + + + + + + diff --git a/packaging/dotnet-core/prepare.py b/packaging/dotnet-core/prepare.py new file mode 100644 index 00000000..660304cc --- /dev/null +++ b/packaging/dotnet-core/prepare.py @@ -0,0 +1,268 @@ +#!/usr/bin/python3 + +import datetime +import json +import os.path +import sys +import xml.etree.ElementTree as ET + +# +# OUTPUT CONFIGURATION +# + +# Package metadata +NUPKG_ID = 'libsodium' +NUPKG_VERSION = '1.0.11' + +# The names of the libsodium binaries in the package +LIBSODIUM_DLL = 'libsodium.dll' +LIBSODIUM_DYLIB = 'libsodium.dylib' +LIBSODIUM_SO = 'libsodium.so' + +# +# INPUT CONFIGURATION +# + +# The archives to download +WIN_FILE = 'libsodium-1.0.11-msvc.zip' +DEB_FILE = 'libsodium18_1.0.11-1_amd64.deb' +RPM_FILE = 'libsodium18-1.0.11-14.1.x86_64.rpm' +OSX_FILE = 'libsodium-1.0.11.{0}.bottle.tar.gz' + +# The URLs of the archives +OFFICIAL_URL = 'https://download.libsodium.org/libsodium/releases/{0}' +OPENSUSE_URL = 'http://download.opensuse.org/repositories/home:/nsec/{0}/{1}/{2}' +HOMEBREW_URL = 'https://bintray.com/homebrew/bottles/download_file?file_path={0}' + +# The files within the archives to extract +WIN_LIB = '{0}/Release/v140/dynamic/libsodium.dll' +DEB_LIB = './usr/lib/x86_64-linux-gnu/libsodium.so.18.1.1' +RPM_LIB = './usr/lib64/libsodium.so.18.1.1' +OSX_LIB = 'libsodium/1.0.11/lib/libsodium.18.dylib' + +# Commands to extract a file from an archive +DEB_EXTRACT = 'ar -p {0} data.tar.xz | tar xJ "{1}"' +RPM_EXTRACT = 'rpm2cpio {0} | cpio -i "{1}"' +TAR_EXTRACT = 'tar xzf {0} "{1}"' +ZIP_EXTRACT = 'unzip {0} "{1}"' + +# The inputs +INPUTS = [ + + ( 'win10-x64', + WIN_FILE, + OFFICIAL_URL.format(WIN_FILE), + WIN_LIB.format('x64'), + ZIP_EXTRACT, + LIBSODIUM_DLL), + + ( 'win10-x86', + WIN_FILE, + OFFICIAL_URL.format(WIN_FILE), + WIN_LIB.format('Win32'), + ZIP_EXTRACT, + LIBSODIUM_DLL), + + ( 'debian.8-x64', + DEB_FILE, + OPENSUSE_URL.format('Debian_8.0', 'amd64', DEB_FILE), + DEB_LIB, + DEB_EXTRACT, + LIBSODIUM_SO), + + ( 'ubuntu.14.04-x64', + DEB_FILE, + OPENSUSE_URL.format('xUbuntu_14.04', 'amd64', DEB_FILE), + DEB_LIB, + DEB_EXTRACT, + LIBSODIUM_SO), + + ( 'ubuntu.16.04-x64', + DEB_FILE, + OPENSUSE_URL.format('xUbuntu_16.04', 'amd64', DEB_FILE), + DEB_LIB, + DEB_EXTRACT, + LIBSODIUM_SO), + + ( 'ubuntu.16.10-x64', + DEB_FILE, + OPENSUSE_URL.format('xUbuntu_16.10', 'amd64', DEB_FILE), + DEB_LIB, + DEB_EXTRACT, + LIBSODIUM_SO), + + ( 'centos.7-x64', + RPM_FILE, + OPENSUSE_URL.format('CentOS_7', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'fedora.23-x64', + RPM_FILE, + OPENSUSE_URL.format('Fedora_23', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'fedora.24-x64', + RPM_FILE, + OPENSUSE_URL.format('Fedora_24', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'fedora.25-x64', + RPM_FILE, + OPENSUSE_URL.format('Fedora_25', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'opensuse.42.1-x64', + RPM_FILE, + OPENSUSE_URL.format('openSUSE_Leap_42.1', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'opensuse.42.2-x64', + RPM_FILE, + OPENSUSE_URL.format('openSUSE_Leap_42.2', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'rhel.7-x64', + RPM_FILE, + OPENSUSE_URL.format('RHEL_7', 'x86_64', RPM_FILE), + RPM_LIB, + RPM_EXTRACT, + LIBSODIUM_SO), + + ( 'osx.10.10-x64', + OSX_FILE.format('yosemite'), + HOMEBREW_URL.format(OSX_FILE.format('yosemite')), + OSX_LIB, + TAR_EXTRACT, + LIBSODIUM_DYLIB), + + ( 'osx.10.11-x64', + OSX_FILE.format('el_capitan'), + HOMEBREW_URL.format(OSX_FILE.format('el_capitan')), + OSX_LIB, + TAR_EXTRACT, + LIBSODIUM_DYLIB), + + ( 'osx.10.12-x64', + OSX_FILE.format('sierra'), + HOMEBREW_URL.format(OSX_FILE.format('sierra')), + OSX_LIB, + TAR_EXTRACT, + LIBSODIUM_DYLIB), + +] + +# The version cookie +COOKIE_FILE = 'version.json' + +# +# INTERMEDIATE FILES +# + +CACHEDIR = 'cache' +TEMPDIR = 'build' + +# +# DO NOT EDIT BELOW THIS LINE +# + +class Item: + def __init__(self, input, cachedir, tempdir): + rid, archive, url, file, extract, lib = input + + self.rid = rid + self.archive = archive + self.url = url + self.file = file + self.extract = extract + self.lib = lib + + self.cachefile = os.path.join(cachedir, rid, archive) + self.sourcedir = os.path.join(tempdir, rid) + self.sourcefile = os.path.join(tempdir, rid, os.path.normpath(file)) + self.targetfile = os.path.join('runtimes', rid, 'native', lib) + +def create_nuspec(template, nuspec, version, items): + tree = ET.parse(template) + package = tree.getroot() + metadata = package.find('metadata') + metadata.find('version').text = version + files = package.find('files') + for item in items: + ET.SubElement(files, 'file', src=item.sourcefile, target=item.targetfile).tail = '\n' + tree.write(nuspec, 'ascii', '') + +def create_makefile(makefile, nupkg, nuspec, items): + with open(makefile, 'w') as f: + for item in items: + f.write('FILES += {0}\n'.format(item.sourcefile)) + f.write('\n') + f.write('{0}: {1} $(FILES)\n\tdotnet nuget pack $<\n'.format(nupkg, nuspec)) + for item in items: + f.write('\n') + f.write('{0}:\n\t@mkdir -p $(dir $@)\n\tcurl -f#Lo $@ "{1}"\n'.format(item.cachefile, item.url)) + for item in items: + f.write('\n') + f.write('{0}: {1}\n\t@mkdir -p $(dir $@)\n\tcd {2} && {3}\n'.format( + item.sourcefile, + item.cachefile, + item.sourcedir, + item.extract.format(os.path.relpath(item.cachefile, item.sourcedir), item.file))) + +def make_prerelease_version(version, suffix, cookie_file): + cookies = dict() + if os.path.isfile(cookie_file): + with open(cookie_file, 'r') as f: + cookies = json.load(f) + cookie = cookies.get(suffix, '---').split('-') + year, month, day, *rest = datetime.datetime.utcnow().timetuple() + major = '{0:03}{1:02}'.format(year * 12 + month - 23956, day) + minor = int(cookie[3]) + 1 if cookie[:3] == [version, suffix, major] else 1 + result = '{0}-{1}-{2}-{3:02}'.format(version, suffix, major, minor) + cookies[suffix] = result + with open(cookie_file, 'w') as f: + json.dump(cookies, f, indent=4, sort_keys=True) + return result + +def main(args): + if len(args) > 2 or len(args) > 1 and not args[1].isalpha: + print('usage: {0} [label]'.format(os.path.basename(args[0]))) + sys.exit(1) + + version = NUPKG_VERSION + + if len(args) > 1: + suffix = args[1].lower() + else: + suffix = 'preview' + + if suffix != 'release': + version = make_prerelease_version(version, suffix, COOKIE_FILE) + print('updated', COOKIE_FILE) + + template = NUPKG_ID + '.nuspec' + nuspec = NUPKG_ID + '.' + version + '.nuspec' + nupkg = NUPKG_ID + '.' + version + '.nupkg' + + tempdir = os.path.join(TEMPDIR, version) + items = [Item(input, CACHEDIR, tempdir) for input in INPUTS] + + create_nuspec(template, nuspec, version, items) + print('created', nuspec) + + create_makefile('Makefile', nupkg, nuspec, items) + print('created', 'Makefile', 'to make', nupkg) + +if __name__ == '__main__': + main(sys.argv) diff --git a/packaging/dotnet-core/version.json b/packaging/dotnet-core/version.json new file mode 100644 index 00000000..26166e89 --- /dev/null +++ b/packaging/dotnet-core/version.json @@ -0,0 +1,3 @@ +{ + "preview": "1.0.11-preview-24920-01" +} \ No newline at end of file