1#!/usr/bin/env python3
2
3import os.path
4import re
5import sys
6
7WINDOWS = [
8  # --------------------- ----------------- #
9  # Runtime ID            Platform          #
10  # --------------------- ----------------- #
11  ( 'win-x64',            'x64'             ),
12  ( 'win-x86',            'Win32'           ),
13  # --------------------- ----------------- #
14]
15
16MACOS = [
17  # --------------------- ----------------- #
18  # Runtime ID            Codename          #
19  # --------------------- ----------------- #
20  ( 'osx-x64',            'sierra'          ),
21  # --------------------- ----------------- #
22]
23
24LINUX = [
25  # --------------------- ----------------- #
26  # Runtime ID            Docker Image      #
27  # --------------------- ----------------- #
28  ( 'linux-x64',          'debian:stretch'  ),
29  # --------------------- ----------------- #
30]
31
32EXTRAS = [ 'LICENSE', 'AUTHORS', 'ChangeLog' ]
33
34PROPSFILE = 'libsodium.props'
35MAKEFILE = 'Makefile'
36BUILDDIR = 'build'
37CACHEDIR = 'cache'
38TEMPDIR = 'temp'
39
40PACKAGE = 'libsodium'
41LIBRARY = 'libsodium'
42
43DOCKER = 'sudo docker'
44
45class Version:
46
47  def __init__(self, libsodium_version, package_version):
48    self.libsodium_version = libsodium_version
49    self.package_version = package_version
50
51    self.builddir = os.path.join(BUILDDIR, libsodium_version)
52    self.tempdir = os.path.join(TEMPDIR, libsodium_version)
53    self.projfile = os.path.join(self.builddir, '{0}.{1}.pkgproj'.format(PACKAGE, package_version))
54    self.propsfile = os.path.join(self.builddir, '{0}.props'.format(PACKAGE))
55    self.pkgfile = os.path.join(BUILDDIR, '{0}.{1}.nupkg'.format(PACKAGE, package_version))
56
57class WindowsItem:
58
59  def __init__(self, version, rid, platform):
60    self.url = 'https://download.libsodium.org/libsodium/releases/libsodium-{0}-msvc.zip'.format(version.libsodium_version)
61    self.cachefile = os.path.join(CACHEDIR, re.sub(r'[^A-Za-z0-9.]', '-', self.url))
62    self.packfile = os.path.join(version.builddir, 'runtimes', rid, 'native', LIBRARY + '.dll')
63    self.itemfile = '{0}/Release/v140/dynamic/libsodium.dll'.format(platform)
64    self.tempdir = os.path.join(version.tempdir, rid)
65    self.tempfile = os.path.join(self.tempdir, os.path.normpath(self.itemfile))
66
67  def make(self, f):
68    f.write('\n')
69    f.write('{0}: {1}\n'.format(self.packfile, self.tempfile))
70    f.write('\t@mkdir -p $(dir $@)\n')
71    f.write('\tcp -f $< $@\n')
72    f.write('\n')
73    f.write('{0}: {1}\n'.format(self.tempfile, self.cachefile))
74    f.write('\t@mkdir -p $(dir $@)\n')
75    f.write('\tcd {0} && unzip -q -DD -o {1} \'{2}\'\n'.format(
76      self.tempdir,
77      os.path.relpath(self.cachefile, self.tempdir),
78      self.itemfile
79    ))
80
81class MacOSItem:
82
83  def __init__(self, version, rid, codename):
84    self.url = 'https://bintray.com/homebrew/bottles/download_file?file_path=libsodium-{0}.{1}.bottle.tar.gz'.format(version.libsodium_version, codename)
85    self.cachefile = os.path.join(CACHEDIR, re.sub(r'[^A-Za-z0-9.]', '-', self.url))
86    self.packfile = os.path.join(version.builddir, 'runtimes', rid, 'native', LIBRARY + '.dylib')
87    self.itemfile = 'libsodium/{0}/lib/libsodium.dylib'.format(version.libsodium_version)
88    self.tempdir = os.path.join(version.tempdir, rid)
89    self.tempfile = os.path.join(self.tempdir, os.path.normpath(self.itemfile))
90
91  def make(self, f):
92    f.write('\n')
93    f.write('{0}: {1}\n'.format(self.packfile, self.tempfile))
94    f.write('\t@mkdir -p $(dir $@)\n')
95    f.write('\tcp -f $< $@\n')
96    f.write('\n')
97    f.write('{0}: {1}\n'.format(self.tempfile, self.cachefile))
98    f.write('\t@mkdir -p $(dir $@)\n')
99    f.write('\tcd {0} && tar xzmf {1} \'{2}\'\n'.format(
100      self.tempdir,
101      os.path.relpath(self.cachefile, self.tempdir),
102      os.path.dirname(self.itemfile)
103    ))
104
105class LinuxItem:
106
107  def __init__(self, version, rid, docker_image):
108    self.url = 'https://download.libsodium.org/libsodium/releases/libsodium-{0}.tar.gz'.format(version.libsodium_version)
109    self.cachefile = os.path.join(CACHEDIR, re.sub(r'[^A-Za-z0-9.]', '-', self.url))
110    self.packfile = os.path.join(version.builddir, 'runtimes', rid, 'native', LIBRARY + '.so')
111    self.tempdir = os.path.join(version.tempdir, rid)
112    self.tempfile = os.path.join(self.tempdir, 'libsodium.so')
113    self.docker_image = docker_image
114    self.recipe = rid
115
116  def make(self, f):
117    recipe = self.recipe
118    while not os.path.exists(os.path.join('recipes', recipe)):
119      m = re.fullmatch(r'([^.-]+)((([.][^.-]+)*)[.][^.-]+)?([-].*)?', recipe)
120      if m.group(5) is None:
121        recipe = 'build'
122        break
123      elif m.group(2) is None:
124        recipe = m.group(1)
125      else:
126        recipe = m.group(1) + m.group(3) + m.group(5)
127
128    f.write('\n')
129    f.write('{0}: {1}\n'.format(self.packfile, self.tempfile))
130    f.write('\t@mkdir -p $(dir $@)\n')
131    f.write('\tcp -f $< $@\n')
132    f.write('\n')
133    f.write('{0}: {1}\n'.format(self.tempfile, self.cachefile))
134    f.write('\t@mkdir -p $(dir $@)\n')
135    f.write('\t{0} run --rm '.format(DOCKER) +
136            '-v $(abspath recipes):/io/recipes ' +
137            '-v $(abspath $<):/io/libsodium.tar.gz ' +
138            '-v $(abspath $(dir $@)):/io/output ' +
139            '{0} sh -x -e /io/recipes/{1}\n'.format(self.docker_image, recipe))
140
141class ExtraItem:
142
143  def __init__(self, version, filename):
144    self.url = 'https://download.libsodium.org/libsodium/releases/libsodium-{0}.tar.gz'.format(version.libsodium_version)
145    self.cachefile = os.path.join(CACHEDIR, re.sub(r'[^A-Za-z0-9.]', '-', self.url))
146    self.packfile = os.path.join(version.builddir, filename)
147    self.itemfile = 'libsodium-{0}/{1}'.format(version.libsodium_version, filename)
148    self.tempdir = os.path.join(version.tempdir, 'extras')
149    self.tempfile = os.path.join(self.tempdir, os.path.normpath(self.itemfile))
150
151  def make(self, f):
152    f.write('\n')
153    f.write('{0}: {1}\n'.format(self.packfile, self.tempfile))
154    f.write('\t@mkdir -p $(dir $@)\n')
155    f.write('\tcp -f $< $@\n')
156    f.write('\n')
157    f.write('{0}: {1}\n'.format(self.tempfile, self.cachefile))
158    f.write('\t@mkdir -p $(dir $@)\n')
159    f.write('\tcd {0} && tar xzmf {1} \'{2}\'\n'.format(
160      self.tempdir,
161      os.path.relpath(self.cachefile, self.tempdir),
162      self.itemfile
163    ))
164
165def main(args):
166  m = re.fullmatch(r'((\d+\.\d+\.\d+)(\.\d+)?)(?:-(\w+(?:[_.-]\w+)*))?', args[1]) if len(args) == 2 else None
167
168  if m is None:
169    print('Usage:')
170    print('       python3 prepare.py <version>')
171    print()
172    print('Examples:')
173    print('       python3 prepare.py 1.0.16-preview-01')
174    print('       python3 prepare.py 1.0.16-preview-02')
175    print('       python3 prepare.py 1.0.16-preview-03')
176    print('       python3 prepare.py 1.0.16')
177    print('       python3 prepare.py 1.0.16.1-preview-01')
178    print('       python3 prepare.py 1.0.16.1')
179    print('       python3 prepare.py 1.0.16.2')
180    return 1
181
182  version = Version(m.group(2), m.group(0))
183
184  items = [ WindowsItem(version, rid, platform)   for (rid, platform) in WINDOWS   ] + \
185          [ MacOSItem(version, rid, codename)     for (rid, codename) in MACOS     ] + \
186          [ LinuxItem(version, rid, docker_image) for (rid, docker_image) in LINUX ] + \
187          [ ExtraItem(version, filename)          for filename in EXTRAS           ]
188
189  downloads = {item.cachefile: item.url for item in items}
190
191  with open(MAKEFILE, 'w') as f:
192    f.write('all: {0}\n'.format(version.pkgfile))
193
194    for download in sorted(downloads):
195      f.write('\n')
196      f.write('{0}:\n'.format(download))
197      f.write('\t@mkdir -p $(dir $@)\n')
198      f.write('\tcurl -f#Lo $@ \'{0}\'\n'.format(downloads[download]))
199
200    for item in items:
201      item.make(f)
202
203    f.write('\n')
204    f.write('{0}: {1}\n'.format(version.propsfile, PROPSFILE))
205    f.write('\t@mkdir -p $(dir $@)\n')
206    f.write('\tcp -f $< $@\n')
207
208    f.write('\n')
209    f.write('{0}: {1}\n'.format(version.projfile, version.propsfile))
210    f.write('\t@mkdir -p $(dir $@)\n')
211    f.write('\techo \'' +
212            '<Project Sdk="Microsoft.NET.Sdk">' +
213            '<Import Project="{0}" />'.format(os.path.relpath(version.propsfile, os.path.dirname(version.projfile))) +
214            '<PropertyGroup>' +
215            '<Version>{0}</Version>'.format(version.package_version) +
216            '</PropertyGroup>' +
217            '</Project>\' > $@\n')
218
219    f.write('\n')
220    f.write('{0}:'.format(version.pkgfile))
221    f.write(' \\\n\t\t{0}'.format(version.projfile))
222    f.write(' \\\n\t\t{0}'.format(version.propsfile))
223    for item in items:
224      f.write(' \\\n\t\t{0}'.format(item.packfile))
225    f.write('\n')
226    f.write('\t@mkdir -p $(dir $@)\n')
227    f.write('\t{0} run --rm '.format(DOCKER) +
228            '-v $(abspath recipes):/io/recipes ' +
229            '-v $(abspath $(dir $<)):/io/input ' +
230            '-v $(abspath $(dir $@)):/io/output ' +
231            '{0} sh -x -e /io/recipes/{1} {2}\n'.format('microsoft/dotnet:2.0-sdk', 'pack', os.path.relpath(version.projfile, version.builddir)))
232
233    f.write('\n')
234    f.write('test: {0}\n'.format(version.pkgfile))
235    f.write('\t{0} run --rm '.format(DOCKER) +
236            '-v $(abspath recipes):/io/recipes ' +
237            '-v $(abspath $(dir $<)):/io/packages ' +
238            '{0} sh -x -e /io/recipes/{1} "{2}"\n'.format('microsoft/dotnet:2.0-sdk', 'test', version.package_version))
239
240  print('prepared', MAKEFILE, 'to make', version.pkgfile, 'for libsodium', version.libsodium_version)
241  return 0
242
243if __name__ == '__main__':
244  sys.exit(main(sys.argv))
245