root/trunk/sss/net.d

Revision 925, 22.0 kB (checked in by Gregor, 3 months ago)

sss/*: Much less verbose by default.

Line 
1 /**
2  * DSSS command "net"
3  *
4  * Authors:
5  *  Gregor Richards
6  *
7  * License:
8  *  Copyright (c) 2006, 2007  Gregor Richards
9  * 
10  *  Permission is hereby granted, free of charge, to any person obtaining a
11  *  copy of this software and associated documentation files (the "Software"),
12  *  to deal in the Software without restriction, including without limitation
13  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  *  and/or sell copies of the Software, and to permit persons to whom the
15  *  Software is furnished to do so, subject to the following conditions:
16  * 
17  *  The above copyright notice and this permission notice shall be included in
18  *  all copies or substantial portions of the Software.
19  * 
20  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  *  DEALINGS IN THE SOFTWARE.
27  */
28
29 module sss.net;
30
31 import std.cstream;
32 import std.stdio;
33 import std.string;
34 alias std.string.split split;
35 import std.file;
36 alias std.file.write write;
37 import std.path;
38 import std.random;
39 import std.regexp;
40
41 import sss.build;
42 import sss.clean;
43 import sss.conf;
44 import sss.install;
45 import sss.system;
46 import sss.uninstall;
47
48 import hcf.path;
49 import hcf.process;
50
51 /// If set, the given mirror will be used
52 char[] forceMirror;
53
54 /*import mango.http.client.HttpClient;
55 import mango.http.client.HttpGet;*/
56
57 /** Entry to the "net" command */
58 int net(char[][] args)
59 {
60     // cannot be used from the source dir
61     if (inSourceDir) {
62         writefln("The 'net' subcommand cannot be used with DSSS running from the source");
63         writefln("directory. You must install DSSS.");
64         return 1;
65     }
66    
67     // make sure our sources list is up to date
68     static bool srcListUpdated = false;
69     if (!srcListUpdated) {
70         srcListUpdated = true;
71        
72         // check for cruft from pre-0.3 DSSS
73         if (exists(srcListPrefix ~ std.path.sep ~ ".svn")) {
74             rmRecursive(srcListPrefix);
75         }
76        
77         writefln("Synchronizing...");
78        
79         if (!exists(srcListPrefix ~ std.path.sep ~ "mirror")) {
80             // find the full list.list file name
81             char[] listlist = installPrefix ~ std.path.sep ~
82                 ".." ~ std.path.sep ~
83                 "etc" ~ std.path.sep ~
84                 "dsss" ~ std.path.sep ~
85                 "list.list";
86             version (Posix) {
87                 if (!std.file.exists(listlist)) {
88                     listlist = "/etc/dsss/list.list";
89                 }
90             }
91            
92             // select a source list mirror
93             char[][] mirrorList = std.string.split(
94                 std.string.replace(
95                     cast(char[]) std.file.read(listlist),
96                     "\r", ""),
97                 "\n");
98             while (mirrorList[$-1] == "") mirrorList = mirrorList[0..$-1];
99            
100             int sel = -1;
101            
102             if (forceMirror.length == 0) {
103                 if (mirrorList.length == 1) {
104                     // easy choice :)
105                     sel = 0;
106                 } else {
107                     writefln("Please choose a mirror for the source list:");
108                     writefln("(Note that you may choose another mirror at any time by removing the directory");
109                     writefln("%s)", srcListPrefix);
110                     writefln("");
111                    
112                     foreach (i, mirror; mirrorList) {
113                         writefln("%d) %s", i + 1, mirror);
114                     }
115                    
116                     // choose
117                     char[] csel;
118                     while (sel < 0 || sel >= mirrorList.length) {
119                         csel = din.readLine();
120                         sel = atoi(csel) - 1;
121                     }
122                 }
123             }
124            
125             char[] mirror;
126             if (sel == -1) {
127                 mirror = forceMirror;
128             } else {
129                 mirror = mirrorList[sel];
130             }
131            
132             // get it
133             mkdirP(srcListPrefix);
134             std.file.write(srcListPrefix ~ std.path.sep ~ "mirror",
135                            mirror);
136             sayAndSystem("curl -s -S -k " ~ mirror ~ "/source.list "
137                         "-o " ~ srcListPrefix ~ std.path.sep ~ "source.list");
138             sayAndSystem("curl -s -S -k " ~ mirror ~ "/pkgs.list "
139                         "-o " ~ srcListPrefix ~ std.path.sep ~ "pkgs.list");
140             sayAndSystem("curl -s -S -k " ~ mirror ~ "/mirrors.list "
141                         "-o " ~ srcListPrefix ~ std.path.sep ~ "mirrors.list");
142         } else {
143             char[] mirror;
144             if (forceMirror.length == 0) {
145                 mirror = cast(char[]) std.file.read(
146                     srcListPrefix ~ std.path.sep ~ "mirror");
147             } else {
148                 mirror = forceMirror;
149             }
150            
151             char[] srcList = srcListPrefix ~ std.path.sep ~ "source.list";
152             char[] pkgsList = srcListPrefix ~ std.path.sep ~ "pkgs.list";
153             char[] mirrorsList = srcListPrefix ~ std.path.sep ~ "mirrors.list";
154            
155             sayAndSystem("curl -s -S -k " ~ mirror ~ "/source.list "
156                         "-o " ~ srcList ~
157                         " -z " ~ srcList);
158             sayAndSystem("curl -s -S -k " ~ mirror ~ "/pkgs.list "
159                         "-o " ~ pkgsList ~
160                         " -z " ~ pkgsList);
161             sayAndSystem("curl -s -S -k " ~ mirror ~ "/mirrors.list "
162                         "-o " ~ mirrorsList ~
163                         " -z " ~ mirrorsList);
164         }
165        
166         writefln("");
167     }
168    
169     // load it
170     NetConfig conf = ReadNetConfig();
171    
172     // now switch on the command
173     if (args.length < 1) {
174         writefln("The net command requires a second command as a parameter.");
175         return 1;
176     }
177     switch (args[0]) {
178         case "deps":
179         case "depslist":
180         {
181             DSSSConf dconf = readConfig(null);
182             char[][] deps;
183             if (args[0] == "deps") {
184                 deps = sourceToDeps(true, conf, dconf);
185             } else {
186                 deps = sourceToDeps(false, conf, dconf);
187             }
188            
189             if (args[0] == "deps") {
190                 // install dependencies
191                 foreach (dep; deps) {
192                     if (dep == "" || dep == dconf.settings[""]["name"]) continue;
193                
194                     char[][] netcommand;
195                     netcommand ~= "assert";
196                     netcommand ~= dep;
197                
198                     writefln("\n\nInstalling %s\n", dep);
199                     int netret = net(netcommand);
200                     if (netret) return netret;
201                 }
202                
203             } else {
204                 // just list them
205                 deps = deps.dup.sort;
206                 char[] last = "";
207                 foreach (dep; deps) {
208                     if (dep != last && dep != "" &&
209                         dep != dconf.settings[""]["name"]) {
210                         writefln("%s", dep);
211                         last = dep;
212                     }
213                 }
214                
215             }
216            
217             return 0;
218         }
219        
220         case "assert":
221         {
222             // make sure that the tool is installed, install it if not
223            
224             // check for manifest files in every usedir
225             bool found = false;
226             char[] manifestFile = manifestPrefix ~ std.path.sep ~ args[1] ~ ".manifest";
227             if (exists(manifestFile)) {
228                 found = true;
229             } else {
230                
231                 foreach (dir; useDirs) {
232                     manifestFile = dir ~ std.path.sep ~
233                         "share" ~ std.path.sep ~
234                         "dsss" ~ std.path.sep ~
235                         "manifest" ~ std.path.sep ~
236                         args[1] ~ ".manifest";
237                     if (exists(manifestFile)) {
238                         found = true;
239                         break;
240                     }
241                 }
242             }
243            
244             if (found) {
245                 writefln("%s is already installed.\n", args[1]);
246                 return 0;
247             }
248            
249             // fall through
250         }
251        
252         case "fetch":
253         case "install":
254         {
255             // download and install the specified package and its dependencies
256             if (args.length < 2) {
257                 writefln("No package name specified.");
258                 return 1;
259             }
260            
261             // 0) sanity
262             if (!(args[1] in conf.vers)) {
263                 writefln("That package does not appear to exist!");
264                 return 1;
265             }
266            
267             // 1) make the source directory
268             char[] srcDir = scratchPrefix ~ std.path.sep ~ "DSSS_" ~ args[1];
269             char[] tmpDir = srcDir;
270             mkdirP(srcDir);
271             writefln("Working in %s", srcDir);
272            
273             // 2) chdir
274             char[] origcwd = getcwd();
275             chdir(srcDir);
276            
277             // make sure the directory gets removed
278             scope(exit) {
279                 chdir(origcwd);
280                 rmRecursive(tmpDir);
281             }
282            
283             // 3) get sources
284             if (!getSources(args[1], conf)) return 1;
285             srcDir = getcwd();
286            
287             // if we're just fetching, make the archive
288             if (args[0] == "fetch") {
289                 char[] archname = args[1] ~ ".tar.gz";
290                
291                 // compress
292                 version (Windows) {
293                     // CyberShadow 2007.02.21: this code actually works now
294                     char[][] files = listdir("");
295                     auto regexp = RegExp(r"^[^\.]");
296                     char[] cmdline = "bsdtar -zcf " ~ archname;
297                     foreach(file;files)
298                         if(regexp.test(file))
299                             cmdline ~= " " ~ file;
300                     vSaySystemDie(cmdline);
301                 } else {
302                     vSaySystemDie("tar -cf - * | gzip -c > " ~ archname);
303                 }
304                
305                 // move into place
306                 try {
307                     std.file.rename(archname,
308                                     origcwd ~ std.path.sep ~ archname);
309                 } catch (Exception x) {
310                     // can't rename, copy
311                     std.file.copy(archname,
312                                   origcwd ~ std.path.sep ~ archname);
313                     std.file.remove(archname);
314                 }
315                
316                 writefln("Archive %s created.", archname);
317                 return 0;
318             } else {
319                 // 4) make sure it's not installed
320                 if (args[1] != "dsss")
321                     uninstall(args[1..2], true);
322                
323                 // 5) install prerequisites
324                 char[][] netcmd;
325                 netcmd ~= "deps";
326                 int netret = net(netcmd);
327                 if (netret) return netret;
328                 chdir(srcDir);
329                
330                 // 6) build
331                 DSSSConf dconf = readConfig(null);
332                 int buildret = build(args[2..$], dconf);
333                 if (buildret) return buildret;
334                
335                 // 7) install
336                 return install(args[2..$]);
337             }
338         }
339        
340         case "list":
341         {
342             // Just list installable packages
343             foreach (pkg; conf.srcURL.keys.sort) {
344                 writefln("%s", pkg);
345             }
346             return 0;
347         }
348        
349         case "search":
350         {
351             // List matching packages
352             if (args.length < 2) {
353                 writefln("Search for what?");
354                 return 1;
355             }
356            
357             foreach (pkg; conf.srcURL.keys.sort) {
358                 if (std.regexp.find(pkg, args[1]) != -1) {
359                     writefln("%s", pkg);
360                 }
361             }
362            
363             return 0;
364         }
365        
366         default:
367             writefln("Unrecognized command: %s", args[0]);
368             return 1;
369     }
370 }
371
372 /** Net config object */
373 class NetConfig {
374     /** The mirror in use */
375     char[] mirror;
376    
377     /** Versions of packages */
378     char[][char[]] vers;
379    
380     /** Dependencies of packages */
381     char[][][char[]] deps;
382    
383     /** Source formats of packages */
384     char[][char[]] srcFormat;
385    
386     /** Source URL of packages */
387     char[][char[]] srcURL;
388    
389     /** Patches */
390     char[][][char[]] srcPatches;
391 }
392
393 /** Read the net configuration info */
394 NetConfig ReadNetConfig()
395 {
396     NetConfig conf = new NetConfig();
397    
398     // read in the mirror
399     conf.mirror = cast(char[]) std.file.read(srcListPrefix ~ std.path.sep ~ "mirror");
400    
401     // read in the main tool/dep/version list
402     char[] pkgslist = std.string.replace(
403         cast(char[]) std.file.read(srcListPrefix ~ std.path.sep ~ "pkgs.list"),
404         "\r", "");
405     foreach (pkg; std.string.split(pkgslist, "\n")) {
406         if (pkg.length == 0 || pkg[0] == '#') continue;
407        
408         char[][] pkinfo = std.string.split(pkg, " ");
409        
410         // format: pkg ver deps
411         if (pkinfo.length < 2) continue;
412         conf.vers[pkinfo[0]] = pkinfo[1];
413         conf.deps[pkinfo[0]] = pkinfo[2..$];
414     }
415    
416     // then read in the source list
417     char[] srclist = cast(char[]) std.file.read(srcListPrefix ~ std.path.sep ~ "source.list");
418     foreach (pkg; std.string.split(srclist, "\n")) {
419         if (pkg.length == 0 || pkg[0] == '#') continue;
420        
421         char[][] pkinfo = std.string.split(pkg, " ");
422        
423         //format: pkg protocol/format URL [patches]
424         if (pkinfo.length < 3) continue;
425         conf.srcFormat[pkinfo[0]] = pkinfo[1];
426         conf.srcURL[pkinfo[0]] = pkinfo[2];
427         conf.srcPatches[pkinfo[0]] = pkinfo[3..$];
428     }
429    
430     return conf;
431 }
432
433 /** Generate a list of dependencies for the current source */
434 char[][] sourceToDeps(bool unresolvedOnly, NetConfig nconf = null, DSSSConf conf = null)
435 {
436     if (nconf is null) {
437         nconf = ReadNetConfig();
438     }
439     if (conf is null) {
440         conf = readConfig(null);
441     }
442    
443     // start with the requires setting
444     char[][] deps;
445     if ("requires" in conf.settings[""]) {
446         deps ~= std.string.split(conf.settings[""]["requires"]);
447     }
448    
449     // then trace uses
450     foreach (section; conf.sections) {
451         char[][] files;
452         char[] type = conf.settings[section]["type"];
453         if (type == "binary") {
454             files ~= section;
455         } else if (type == "library" || type == "sourcelibrary") {
456             files ~= targetToFiles(section, conf);
457         } else if (type == "subdir") {
458             // recurse
459             char[] origcwd = getcwd();
460             chdir(section);
461             deps ~= sourceToDeps(unresolvedOnly, nconf);
462             chdir(origcwd);
463             continue;
464         } else {
465             // ignore
466             continue;
467         }
468        
469         // use dsss_build -files or -notfound to get the list of files
470         char[] filesFlag = "-files";
471         if (unresolvedOnly)
472             filesFlag = "-notfound";
473         systemResponse(dsss_build ~ " " ~ filesFlag ~ " -offiles.tmp " ~
474                        std.string.join(files, " "), "-rf", "temp.rf", true);
475        
476         // read the uses
477         char[][] uses = std.string.split(cast(char[]) std.file.read("files.tmp"),
478                                          "\n");
479         foreach (use; uses) {
480             if (use.length == 0) break;
481
482             // get rid of any trailing \r's or \n's
483             while (use.length &&
484                    (use[$-1] == '\n' ||
485                     use[$-1] == '\r')) {
486                 use = use[0..$-1];
487             }
488             if (use.length == 0) break;
489            
490             // add the dep
491             deps ~= canonicalSource(use, nconf);
492         }
493        
494         tryRemove("files.tmp");
495     }
496    
497     return deps;
498 }
499
500 /** Canonicalize a dependency (.d -> source) */
501 char[] canonicalSource(char[] origsrc, NetConfig nconf)
502 {
503     char[] src = origsrc.dup;
504     version (Windows) {
505         src = std.string.replace(src, "\\", "/");
506     }
507    
508     if ((src.length > 2 &&
509          std.string.tolower(src[$-2 .. $]) == ".d") ||
510         (src.length > 3 &&
511          std.string.tolower(src[$-3 .. $]) == ".di")) {
512         // convert to a proper source
513         if (src in nconf.deps &&
514             nconf.deps[src].length == 1) {
515             src = nconf.deps[src][0].dup;
516         } else {
517             src = "";
518         }
519     }
520    
521     return src;
522 }
523
524 /** Get the source for a given package
525  * Returns true on success, false on failure
526  * NOTE: Your chdir can change! */
527 bool getSources(char[] pkg, NetConfig conf)
528 {
529     /// get sources from upstream, return false on failure
530     bool getUpstream() {
531         // 1) get source
532         char[] srcFormat = conf.srcFormat[pkg];
533         int res;
534         switch (srcFormat) {
535             case "svn":
536                 // Subversion, check it out
537                 res = vSayAndSystem("svn export " ~ conf.srcURL[pkg]);
538                 break;
539                
540             default:
541             {
542                 /* download ...
543                 HttpGet dlhttp = new HttpGet(conf.srcURL[pkg]);
544                 
545                 // save it to a source file
546                 write("src." ~ srcFormat, dlhttp.read());*/
547                
548                 // mango doesn't work properly for me :(
549                 res = vSayAndSystem("curl -k " ~ conf.srcURL[pkg] ~ " -o src." ~ srcFormat);
550                 if (res != 0) return false;
551                
552                 // extract it
553                 switch (srcFormat) {
554                     case "tar.gz":
555                     case "tgz":
556                         version (Windows) {
557                             // assume BsdTar
558                             vSayAndSystem("bsdtar -xf src." ~ srcFormat);
559                             res = 0;
560                         } else {
561                             res = vSayAndSystem("gunzip -c src." ~ srcFormat ~ " | tar -xf -");
562                         }
563                         break;
564                        
565                     case "tar.bz2":