From 99a859932e4bd3de55e8d9a35b3b5253a8ca9588 Mon Sep 17 00:00:00 2001 From: grbd Date: Sat, 1 Oct 2016 02:55:46 +0100 Subject: [PATCH] Added python scripts for building NuGet packages via windows or linux --- .gitignore | 11 +++ NuGet/Readme.md | 67 +++++++++++++++ NuGet/build.py | 59 +++++++++++++ NuGet/misc/GtkSharp.Native.targets | 11 +++ NuGet/misc/GtkSharp.nuspec | 16 ++++ NuGet/misc/GtkSharp.targets | 10 +++ NuGet/pybuild/GtkSharp_Builder.py | 85 +++++++++++++++++++ NuGet/pybuild/Gtk_Builder.py | 77 +++++++++++++++++ NuGet/pybuild/Helper.py | 78 +++++++++++++++++ NuGet/pybuild/Settings.py | 80 +++++++++++++++++ NuGet/pybuild/__init__.py | 0 NuGet/vs/GtkSharp-NugetBuild.sln | 25 ++++++ .../GtkSharp-NugetBuild.pyproj | 70 +++++++++++++++ 13 files changed, 589 insertions(+) create mode 100644 NuGet/Readme.md create mode 100644 NuGet/build.py create mode 100644 NuGet/misc/GtkSharp.Native.targets create mode 100644 NuGet/misc/GtkSharp.nuspec create mode 100644 NuGet/misc/GtkSharp.targets create mode 100644 NuGet/pybuild/GtkSharp_Builder.py create mode 100644 NuGet/pybuild/Gtk_Builder.py create mode 100644 NuGet/pybuild/Helper.py create mode 100644 NuGet/pybuild/Settings.py create mode 100644 NuGet/pybuild/__init__.py create mode 100644 NuGet/vs/GtkSharp-NugetBuild.sln create mode 100644 NuGet/vs/GtkSharp-NugetBuild/GtkSharp-NugetBuild.pyproj diff --git a/.gitignore b/.gitignore index 69345e974..0a4380a2b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,14 @@ stamp-h1 AssemblyInfo.cs Makefile Makefile.in + +# Nuget build files +NuGet/build +NuGet/nupkg + +# Python compiled files +*.egg-* +*.pyc +.eggs +dist +*.suo diff --git a/NuGet/Readme.md b/NuGet/Readme.md new file mode 100644 index 000000000..a9764bdb9 --- /dev/null +++ b/NuGet/Readme.md @@ -0,0 +1,67 @@ +# Readme + +## Overview + +This is a series of python scripts to generate the binaries and NuGet Packages for GtkSharp and Gtk + + * NuGet Packages for the GtkSharp .Net libraries + * NuGet Packages for the Windows 32bit / 64bit Gtk dll's + +## Windows + +### Depends + +The following is used as part of the build process + + * MSYS2 - Windows install + +Also it's assumed that you've already installed the 32bit and 64bit binaries for gtk within the MSYS environment +if your going to generate the Win32 / Win64 Nuget Packages for windows + +``` +pacman -S mingw-w64-i686-pango mingw-w64-i686-atk mingw-w64-i686-gtk3 +pacman -S mingw-w64-x86_64-pango mingw-w64-x86_64-atk mingw-w64-x86_64-gtk3 +``` + +And installed the executor python module +``` +C:\Python35\Scripts\pip.exe install executor +``` + +### Running Build + +To run the build +``` +cd gtk-sharp\NuGet +build.py all +``` + +## Linux + +### Depends + +For Ubuntu we need to install pip for python 3 and a few other packages +``` +sudo apt-get install python3-pip autoconf libtool libtool-bin mono-complete +sudo apt-get install libglib2.0-dev libpango1.0-dev libatk1.0-dev libgtk-3-dev +``` + +Then install the executor python module +``` +pip3 install executor +``` + +The version of Nuget needs to be the latest for linux +``` +sudo wget https://dist.nuget.org/win-x86-commandline/v3.5.0-rc1/NuGet.exe -O /usr/local/bin/nuget.exe +sudo chmod +x /usr/local/bin/nuget.exe +``` + +### Running Build + +To run the build +``` +cd gtk-sharp/NuGet +chmod +x build.py +./build.py all +``` diff --git a/NuGet/build.py b/NuGet/build.py new file mode 100644 index 000000000..0fbb81679 --- /dev/null +++ b/NuGet/build.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +"""Script to build out the .Net dll's and package them into a Nuget Package for gtksharp3""" +import os, sys +from pybuild.GtkSharp_Builder import GtkSharp_Builder +from pybuild.Gtk_Builder import Gtk_Builder +from pybuild.Helper import Helper as helpers + +# Ideally I'd like to see the GtkSharp Build system redone via .Net Core or something other than make +# For now though we rely on the use of make to build the .Net dll's +# under linux we run this natively, under windows we can use MSYS2 + +class Build(object): + + # Class Init + def __init__(self): + self.GtkSharp_Builder = GtkSharp_Builder() + self.Gtk_Builder = Gtk_Builder() + + # Clean the Build directory + def clean(self): + """Clean the build dir""" + helpers.emptydir('./build') + print ("Clean finished") + + # Print Usage + def usage(self): + print ("Please use GtkSharp3_Build.py where is one of") + print (" clean to clean the output directory: ./build") + print (" gtksharp_net45 to build ,Net libs for GtkSharp, via .Net 4.5") + print (" gtksharp_nuget_net45 to build Nuget Packages for GtkSharp, via .Net 4.5") + print (" gtk_nuget_win32 to build the Nuget package for GtkSharp.Win32") + print (" gtk_nuget_win64 to build the Nuget package for GtkSharp.Win64") + print (" all to make all") + + def main(self): + if len(sys.argv) != 2: + self.usage() + return + + if sys.argv[1] == "gtksharp_net45": + self.GtkSharp_Builder.build_net45() + if sys.argv[1] == "gtksharp_nuget_net45": + self.GtkSharp_Builder.build_nuget_net45() + if sys.argv[1] == "gtk_nuget_win32": + self.Gtk_Builder.build_nuget_win32() + if sys.argv[1] == "gtk_nuget_win64": + self.Gtk_Builder.build_nuget_win64() + + if sys.argv[1] == "all": + self.GtkSharp_Builder.build_net45() + self.GtkSharp_Builder.build_nuget_net45() + self.Gtk_Builder.build_nuget_win32() + self.Gtk_Builder.build_nuget_win64() + + if sys.argv[1] == "clean": + self.clean() + +if __name__ == "__main__": + Build().main() diff --git a/NuGet/misc/GtkSharp.Native.targets b/NuGet/misc/GtkSharp.Native.targets new file mode 100644 index 000000000..16cf8d793 --- /dev/null +++ b/NuGet/misc/GtkSharp.Native.targets @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/NuGet/misc/GtkSharp.nuspec b/NuGet/misc/GtkSharp.nuspec new file mode 100644 index 000000000..3583ea169 --- /dev/null +++ b/NuGet/misc/GtkSharp.nuspec @@ -0,0 +1,16 @@ + + + + GBD.GtkSharp + 3.22.0 + Ric Westell + grbd + https://github.com/mono/gtk-sharp/blob/master/COPYING + https://github.com/mono/gtk-sharp + https://upload.wikimedia.org/wikipedia/en/5/5f/Gtk_Sharp_Logo.png + false + Gtk# is a .NET language binding for the GTK+ toolkit and assorted GNOME libraries. Gtk# is free software, licensed under the GNU LGPL. + Copyright 2016 + GTK3 GTK# gtk3-sharp + + \ No newline at end of file diff --git a/NuGet/misc/GtkSharp.targets b/NuGet/misc/GtkSharp.targets new file mode 100644 index 000000000..2ac7cc5a3 --- /dev/null +++ b/NuGet/misc/GtkSharp.targets @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/NuGet/pybuild/GtkSharp_Builder.py b/NuGet/pybuild/GtkSharp_Builder.py new file mode 100644 index 000000000..62d8f47f7 --- /dev/null +++ b/NuGet/pybuild/GtkSharp_Builder.py @@ -0,0 +1,85 @@ +#! python3 +import os, shutil +from pybuild.Settings import Settings +from pybuild.Helper import Helper as helpers +from os.path import join +from xml.etree import ElementTree as et + +class GtkSharp_Builder(object): + + def __init__(self): + """Class Init""" + self.setts = Settings() + + def clean(self): + """Clean the build dir""" + helpers.emptydir('./build') + print ("Clean finished") + + def build_net45(self): + """Build the gtksharp binaries for .Net 4.5""" + self.clean() + os.makedirs(self.setts.BuildDir, exist_ok=True) + buildfile = join(self.setts.BuildDir, 'net45_build.sh') + + # run script via MSYS for windows, or shell for linux + if os.name == 'nt': + print("Building .Net GtkSharp using MSYS2") + with open(buildfile, 'w') as f: + f.write('PATH=$PATH:/c/Program\ Files\ \(x86\)/Microsoft\ SDKs/Windows/v10.0A/bin/NETFX\ 4.6\ Tools/\n') + f.write('PATH=$PATH:/c/Windows/Microsoft.NET/Framework/v4.0.30319/\n') + f.write('cd ' + helpers.winpath_to_msyspath(self.setts.SrcDir + '\n')) + f.write('./autogen.sh --prefix=/tmp/install\n') + f.write('make clean\n') + f.write('make\n') + cmds = [join(self.setts.msys2path, 'usr\\bin\\bash.exe'), '--login', buildfile] + cmd = helpers.run_cmd(cmds, self.setts.SrcDir) + + else: + print("Building using Linux shell") + with open(buildfile, 'w') as f: + f.write('cd ' + self.setts.SrcDir + '\n') + f.write('./autogen.sh --prefix=/tmp/install\n') + f.write('make clean\n') + f.write('make\n') + cmds = [self.setts.bashpath, buildfile] + cmd = helpers.run_cmd(cmds, self.setts.SrcDir) + + def build_nuget_net45(self): + """Package up a nuget file based on the default build""" + self.clean() + net45_build_dir = join(self.setts.BuildDir, 'build', 'net45') + net45_lib_dir = join(self.setts.BuildDir, 'lib', 'net45') + GtkVersion = helpers.get_gtksharp_version(self.setts.SrcDir) + + os.makedirs(self.setts.BuildDir, exist_ok=True) + os.makedirs(net45_build_dir, exist_ok=True) + os.makedirs(net45_lib_dir, exist_ok=True) + + print ('Copying Files') + shutil.copy('./misc/GtkSharp.nuspec', self.setts.BuildDir) + shutil.copy('./misc/GtkSharp.targets', net45_build_dir) + + dll_list = ['atk', 'cairo', 'gdk', 'gio', 'glib', 'gtk', 'pango'] + for item in dll_list: + shutil.copy(join(self.setts.SrcDir, item, item + "-sharp.dll"), net45_lib_dir) + if item != 'cairo': + shutil.copy(join(self.setts.SrcDir, item, item + '-sharp.dll.config'), net45_build_dir) + + # Edit the XML version / package name in the .nuspec file + nuspecfile = join(self.setts.BuildDir, 'GtkSharp.nuspec') + et.register_namespace('', 'http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd') + tree = et.parse(nuspecfile) + xmlns = {'nuspec': '{http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd}'} + tree.find('.//{nuspec}version'.format(**xmlns)).text = GtkVersion + tree.find('.//{nuspec}id'.format(**xmlns)).text = self.setts.Gtksharp_PackageName + tree.write(nuspecfile) + + # Run Nuget + helpers.run_cmd([self.setts.NuGetExe, 'pack', 'GtkSharp.nuspec'], self.setts.BuildDir) + + nugetfile = join(self.setts.BuildDir, self.setts.Gtksharp_PackageName + '.' + GtkVersion + '.nupkg') + os.makedirs(self.setts.NugetPkgs, exist_ok=True) + shutil.copy(nugetfile, self.setts.NugetPkgs) + + print ('Generation of Nuget package complete') diff --git a/NuGet/pybuild/Gtk_Builder.py b/NuGet/pybuild/Gtk_Builder.py new file mode 100644 index 000000000..a549835ba --- /dev/null +++ b/NuGet/pybuild/Gtk_Builder.py @@ -0,0 +1,77 @@ +#! python3 +import os, shutil +from pybuild.Settings import Settings +from pybuild.Helper import Helper as helpers +from os.path import join +from xml.etree import ElementTree as et + +# This script assumes the gtk libraries have already been installed via MSYS2 / MinGW32 / MinGW64 + +class Gtk_Builder(object): + + def __init__(self): + """Class Init""" + self.setts = Settings() + + def clean(self): + """Clean the build dir""" + helpers.emptydir('./build') + print ("Clean finished") + + def build_nuget_win32(self): + self.build_nuget('Win32') + + def build_nuget_win64(self): + self.build_nuget('Win64') + + def build_nuget(self, arch): + """Package up a nuget file based on the default build""" + + if os.name != 'nt': + print("Skipping Native Nuget package build, as this needs to be run on Windows") + return + + self.clean() + net45_build_dir = join(self.setts.BuildDir, 'build', 'net45') + if arch == 'Win32': + mingwdir = self.setts.mingwin32path + else: + mingwdir = self.setts.mingwin64path + + os.makedirs(self.setts.BuildDir, exist_ok=True) + os.makedirs(net45_build_dir, exist_ok=True) + + print ('Copying Files') + shutil.copy('./misc/GtkSharp.nuspec', self.setts.BuildDir) + shutil.copy('./misc/GtkSharp.Native.targets', join(net45_build_dir, 'GtkSharp.' + arch + '.targets')) + + # Copy dlls + dll_list = [] + dll_list += self.setts.get_native_win_dlls() + dll_list += self.setts.get_native_win_deps() + + for item in dll_list: + src = join(mingwdir, item) + helpers.copy_files(src, net45_build_dir) + + # Get version + GtkVersion = helpers.get_gtk_version(self.setts.msys2path) + + # Edit the XML version / package name in the .nuspec file + nuspecfile = join(self.setts.BuildDir, 'GtkSharp.nuspec') + et.register_namespace('', 'http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd') + tree = et.parse(nuspecfile) + xmlns = {'nuspec': '{http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd}'} + tree.find('.//{nuspec}version'.format(**xmlns)).text = GtkVersion + tree.find('.//{nuspec}id'.format(**xmlns)).text = self.setts.Gtksharp_PackageName + '.' + arch + tree.write(nuspecfile) + + # Run Nuget + helpers.run_cmd([self.setts.NuGetExe, 'pack', 'GtkSharp.nuspec'], self.setts.BuildDir) + + nugetfile = join(self.setts.BuildDir, self.setts.Gtksharp_PackageName + '.' + arch + '.' + GtkVersion + '.nupkg') + os.makedirs(self.setts.NugetPkgs, exist_ok=True) + shutil.copy(nugetfile, self.setts.NugetPkgs) + + print ('Generation of Nuget package complete') + diff --git a/NuGet/pybuild/Helper.py b/NuGet/pybuild/Helper.py new file mode 100644 index 000000000..32313de64 --- /dev/null +++ b/NuGet/pybuild/Helper.py @@ -0,0 +1,78 @@ +#! python3 +"""Helper Functions""" + +import os, subprocess, shutil, sys +from os.path import join +from glob import iglob +from executor import ExternalCommand +import ntpath + +class Helper(object): + + def emptydir(top): + """Empty a Directory""" + if(top == '/' or top == "\\"): return + else: + for root, dirs, files in os.walk(top, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + + def run_cmd(cmdarray, workdir, comms = None): + """Run a command on the shell""" + cmd = ExternalCommand(*cmdarray, capture=True, capture_stderr=True, async=True, shell=False, directory=workdir) + cmd.start() + last_out = '' + last_err = '' + while cmd.is_running: + new_out = cmd.decoded_stdout.replace(last_out, '') + new_err = cmd.decoded_stderr.replace(last_err, '') + last_out += new_out + last_err += new_err + new_out = new_out.replace(u"\u2018", "'").replace(u"\u2019", "'") + new_err = new_err.replace(u"\u2018", "'").replace(u"\u2019", "'") + if new_out != '': print(new_out, end='') + if new_err != '': print(new_err, end='') + + if cmd.returncode != 0: + raise RuntimeError('Failure to run command') + return cmd + + def winpath_to_msyspath(winpath): + """Convert a Windows path to a Msys type path""" + winpath = '/' + winpath[0] + winpath[2:].replace('\\', '/') + return winpath + + def get_gtksharp_version(srcdir): + """Get the Version of GTK Sharp in use from the source directory""" + ret = None + with open(join(srcdir, 'configure.ac')) as f: + for line in f: + if line.startswith('AC_INIT'): + ret = line + ret = ret.replace('AC_INIT(gtk-sharp,', '') + ret = ret.replace(' ', '') + ret = ret.replace(')\n', '') + break + return ret + + def get_gtk_version(msyspath): + ret = '' + pacman_path = join(msyspath, 'usr\\bin\\pacman.exe') + # pull version from msys2 / pacman + # pacman -Qi mingw-w64-i686-gtk3 + cmd = Helper.run_cmd([pacman_path, '-Qi', 'mingw-w64-i686-gtk3'], msyspath) + + for line in cmd.output.split('\n'): + if 'Version' in line: + ret = line.replace('Version', '') + ret = ret.replace(' ', '').replace(':', '') + if '-' in ret: + ret = ret[:-2] + break + return ret + + def copy_files(src_glob, dst_folder): + for fname in iglob(src_glob): + shutil.copy(fname, join(dst_folder, ntpath.basename(fname))) \ No newline at end of file diff --git a/NuGet/pybuild/Settings.py b/NuGet/pybuild/Settings.py new file mode 100644 index 000000000..79a7d1860 --- /dev/null +++ b/NuGet/pybuild/Settings.py @@ -0,0 +1,80 @@ +#! python3 +"""Settings for scripts""" + +import os + +class Settings(object): + + def __init__(self): + """Class Init""" + + self.NuGetExe = 'nuget.exe' + self.Gtksharp_PackageName = 'GBD.GtkSharp' + + self.SrcDir = '../' + self.BuildDir = './build' + self.NugetPkgs = './nupkg' + self.SrcDir = os.path.abspath(self.SrcDir) + self.BuildDir = os.path.abspath(self.BuildDir) + self.NugetPkgs = os.path.abspath(self.NugetPkgs) + + self.msys2path = 'C:\\msys64' + self.msys2path = os.path.abspath(self.msys2path) + self.bashpath = '/bin/bash' + + self.mingwin32path = 'C:\\msys64\\mingw32\\bin' + self.mingwin32path = os.path.abspath(self.mingwin32path) + self.mingwin64path = 'C:\\msys64\\mingw64\\bin' + self.mingwin64path = os.path.abspath(self.mingwin64path) + + def get_native_win_dlls(self): + ret = [] + + # Gtk + ret.append('libgtk-3-0.dll') + ret.append('libgdk-3-0.dll') + + # atk + ret.append('libatk-1.0-0.dll') + + # cairo + ret.append('libcairo-2.dll') + ret.append('libcairo-gobject-2.dll') + + # gdk-pixbuf + ret.append('libgdk_pixbuf-2.0-0.dll') + + # glib2 + ret.append('libgio-2.0-0.dll') + ret.append('libglib-2.0-0.dll') + ret.append('libgmodule-2.0-0.dll') + ret.append('libgobject-2.0-0.dll') + + # pango + ret.append('libpango-1.0-0.dll') + ret.append('libpangocairo-1.0-0.dll') + ret.append('libpangoft2-1.0-0.dll') + ret.append('libpangowin32-1.0-0.dll') + return ret + + def get_native_win_deps(self): + ret = [] + # Determined by using PE Explorer + ret.append('libgcc_*.dll') + ret.append('libepoxy-0.dll') + ret.append('libintl-8.dll') + ret.append('libwinpthread-1.dll') + ret.append('libiconv-2.dll') + ret.append('libfontconfig-1.dll') + ret.append('libexpat-1.dll') + ret.append('libfreetype-6.dll') + ret.append('libpixman-1-0.dll') + ret.append('libpng16-16.dll') + ret.append('zlib1.dll') + ret.append('libpcre-1.dll') + ret.append('libffi-6.dll') + ret.append('libharfbuzz-0.dll') + ret.append('libgraphite2.dll') + ret.append('libstdc++-6.dll') + ret.append('libbz2-1.dll') + return ret \ No newline at end of file diff --git a/NuGet/pybuild/__init__.py b/NuGet/pybuild/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/NuGet/vs/GtkSharp-NugetBuild.sln b/NuGet/vs/GtkSharp-NugetBuild.sln new file mode 100644 index 000000000..5301fc15e --- /dev/null +++ b/NuGet/vs/GtkSharp-NugetBuild.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{52C7113D-CB6A-4A0E-AD00-F2840069C3A5}" +EndProject +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "GtkSharp-NugetBuild", "GtkSharp-NugetBuild\GtkSharp-NugetBuild.pyproj", "{222475C5-78B6-4961-9552-D83D2C65A9A1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {222475C5-78B6-4961-9552-D83D2C65A9A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {222475C5-78B6-4961-9552-D83D2C65A9A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {222475C5-78B6-4961-9552-D83D2C65A9A1} = {52C7113D-CB6A-4A0E-AD00-F2840069C3A5} + EndGlobalSection +EndGlobal diff --git a/NuGet/vs/GtkSharp-NugetBuild/GtkSharp-NugetBuild.pyproj b/NuGet/vs/GtkSharp-NugetBuild/GtkSharp-NugetBuild.pyproj new file mode 100644 index 000000000..262f9bcbe --- /dev/null +++ b/NuGet/vs/GtkSharp-NugetBuild/GtkSharp-NugetBuild.pyproj @@ -0,0 +1,70 @@ + + + + Debug + 2.0 + 222475c5-78b6-4961-9552-d83d2c65a9a1 + + + ..\..\build.py + ..\..\;..\..\pybuild\ + ..\..\ + . + GtkSharp-NugetBuild + GtkSharp3-Build + False + {9a7a9026-48c1-4688-9d5d-e5699d47d074} + 3.5 + Standard Python launcher + False + all + + + true + false + + + true + false + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets + + + + + + + build.py + + + pybuild\GtkSharp_Builder.py + + + pybuild\Gtk_Builder.py + + + pybuild\Helper.py + + + pybuild\Settings.py + + + pybuild\__init__.py + + + + + + + + + + + + + + \ No newline at end of file