1#!/usr/bin/env nix-shell
2#!nix-shell update-luarocks-shell.nix -i python3
3
4# format:
5# $ nix run nixpkgs.python3Packages.black -c black update.py
6# type-check:
7# $ nix run nixpkgs.python3Packages.mypy -c mypy update.py
8# linted:
9# $ nix run nixpkgs.python3Packages.flake8 -c flake8 --ignore E501,E265,E402 update.py
10
11import inspect
12import os
13import tempfile
14import shutil
15from dataclasses import dataclass
16import subprocess
17import csv
18import logging
19import textwrap
20from multiprocessing.dummy import Pool
21
22from typing import List, Tuple, Optional
23from pathlib import Path
24
25log = logging.getLogger()
26log.addHandler(logging.StreamHandler())
27
28ROOT = Path(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))).parent.parent # type: ignore
29import pluginupdate
30from pluginupdate import update_plugins, FetchConfig, CleanEnvironment
31
32PKG_LIST="maintainers/scripts/luarocks-packages.csv"
33TMP_FILE="$(mktemp)"
34GENERATED_NIXFILE="pkgs/development/lua-modules/generated-packages.nix"
35LUAROCKS_CONFIG="maintainers/scripts/luarocks-config.lua"
36
37HEADER = """/* {GENERATED_NIXFILE} is an auto-generated file -- DO NOT EDIT!
38Regenerate it with:
39nixpkgs$ ./maintainers/scripts/update-luarocks-packages
40
41You can customize the generated packages in pkgs/development/lua-modules/overrides.nix
42*/
43""".format(GENERATED_NIXFILE=GENERATED_NIXFILE)
44
45FOOTER="""
46}
47/* GENERATED - do not edit this file */
48"""
49
50@dataclass
51class LuaPlugin:
52 name: str
53 '''Name of the plugin, as seen on luarocks.org'''
54 src: str
55 '''address to the git repository'''
56 ref: Optional[str]
57 '''git reference (branch name/tag)'''
58 version: Optional[str]
59 '''Set it to pin a package '''
60 server: Optional[str]
61 '''luarocks.org registers packages under different manifests.
62 Its value can be 'http://luarocks.org/dev'
63 '''
64 luaversion: Optional[str]
65 '''Attribue of the lua interpreter if a package is available only for a specific lua version'''
66 maintainers: Optional[str]
67 ''' Optional string listing maintainers separated by spaces'''
68
69 @property
70 def normalized_name(self) -> str:
71 return self.name.replace(".", "-")
72
73# rename Editor to LangUpdate/ EcosystemUpdater
74class LuaEditor(pluginupdate.Editor):
75 def get_current_plugins(self):
76 return []
77
78 def load_plugin_spec(self, input_file) -> List[LuaPlugin]:
79 luaPackages = []
80 csvfilename=input_file
81 log.info("Loading package descriptions from %s", csvfilename)
82
83 with open(csvfilename, newline='') as csvfile:
84 reader = csv.DictReader(csvfile,)
85 for row in reader:
86 # name,server,version,luaversion,maintainers
87 plugin = LuaPlugin(**row)
88 luaPackages.append(plugin)
89 return luaPackages
90
91 def update(self, args):
92 update_plugins(self, args)
93
94 def generate_nix(
95 self,
96 results: List[Tuple[LuaPlugin, str]],
97 outfilename: str
98 ):
99
100 with tempfile.NamedTemporaryFile("w+") as f:
101 f.write(HEADER)
102 header2 = textwrap.dedent(
103 # header2 = inspect.cleandoc(
104 """
105 { self, stdenv, lib, fetchurl, fetchgit, callPackage, ... } @ args:
106 final: prev:
107 {
108 """)
109 f.write(header2)
110 for (plugin, nix_expr) in results:
111 f.write(f"{plugin.normalized_name} = {nix_expr}")
112 f.write(FOOTER)
113 f.flush()
114
115 # if everything went fine, move the generated file to its destination
116 # using copy since move doesn't work across disks
117 shutil.copy(f.name, outfilename)
118
119 print(f"updated {outfilename}")
120
121 @property
122 def attr_path(self):
123 return "luaPackages"
124
125 def get_update(self, input_file: str, outfile: str, config: FetchConfig):
126 _prefetch = generate_pkg_nix
127
128 def update() -> dict:
129 plugin_specs = self.load_plugin_spec(input_file)
130 sorted_plugin_specs = sorted(plugin_specs, key=lambda v: v.name.lower())
131
132 try:
133 pool = Pool(processes=config.proc)
134 results = pool.map(_prefetch, sorted_plugin_specs)
135 finally:
136 pass
137
138 self.generate_nix(results, outfile)
139
140 redirects = {}
141 return redirects
142
143 return update
144
145 def rewrite_input(self, input_file: str, *args, **kwargs):
146 # vim plugin reads the file before update but that shouldn't be our case
147 # not implemented yet
148 # fieldnames = ['name', 'server', 'version', 'luaversion', 'maintainers']
149 # input_file = "toto.csv"
150 # with open(input_file, newline='') as csvfile:
151 # writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
152 # writer.writeheader()
153 # for row in reader:
154 # # name,server,version,luaversion,maintainers
155 # plugin = LuaPlugin(**row)
156 # luaPackages.append(plugin)
157 pass
158
159def generate_pkg_nix(plug: LuaPlugin):
160 '''
161 Generate nix expression for a luarocks package
162 Our cache key associates "p.name-p.version" to its rockspec
163 '''
164 log.debug("Generating nix expression for %s", plug.name)
165 custom_env = os.environ.copy()
166 custom_env['LUAROCKS_CONFIG'] = LUAROCKS_CONFIG
167
168 # we add --dev else luarocks wont find all the "scm" (=dev) versions of the
169 # packages
170 # , "--dev"
171 cmd = [ "luarocks", "nix" ]
172
173 if plug.maintainers:
174 cmd.append(f"--maintainers={plug.maintainers}")
175
176 # if plug.server == "src":
177 if plug.src != "":
178 if plug.src is None:
179 msg = "src must be set when 'version' is set to \"src\" for package %s" % plug.name
180 log.error(msg)
181 raise RuntimeError(msg)
182 log.debug("Updating from source %s", plug.src)
183 cmd.append(plug.src)
184 # update the plugin from luarocks
185 else:
186 cmd.append(plug.name)
187 if plug.version and plug.version != "src":
188
189 cmd.append(plug.version)
190
191 if plug.server != "src" and plug.server:
192 cmd.append(f"--only-server={plug.server}")
193
194 if plug.luaversion:
195 cmd.append(f"--lua-version={plug.luaversion}")
196
197 log.debug("running %s", ' '.join(cmd))
198
199 output = subprocess.check_output(cmd, env=custom_env, text=True)
200 output = "callPackage(" + output.strip() + ") {};\n\n"
201 return (plug, output)
202
203def main():
204
205 editor = LuaEditor("lua", ROOT, '',
206 default_in = ROOT.joinpath(PKG_LIST),
207 default_out = ROOT.joinpath(GENERATED_NIXFILE)
208 )
209
210 editor.run()
211
212if __name__ == "__main__":
213
214 main()
215
216# vim: set ft=python noet fdm=manual fenc=utf-8 ff=unix sts=0 sw=4 ts=4 :