3 * Subversion based package repository and package manager.
10 classvar <>svnpath="/usr/local/bin/svn";
15 svnpath = [svnpath, "/usr/local/bin/svn", "/usr/bin/svn", "/opt/local/bin/svn", "/sw/bin/svn"].detect({ |path|
18 if(svnpath.isNil and:{thisProcess.platform.hasFeature(\unixPipes)}){
19 // Try and detect whether svn is in the path, and could be called just via "svn"
20 res = "svn --version --quiet".unixCmdGetStdOut;
21 if(res.size != 0 and: {res[0].asString.asInteger > 0}){
29 if(thisProcess.platform.name==\windows){
31 << "\tSVN not yet possible on windows. Quarks placed in the directory"
32 << "\n\n\t\t" << Platform.userAppSupportDir << "\\quarks"
33 << "\n\n\t" << "will be available.\n"
36 << "\tSVN not found! Quarks placed in the directory"
37 << "\n\n\t\t" << Platform.userAppSupportDir << "/quarks"
38 << "\n\n\t" << "will be available, but you need svn to checkout updated versions."
39 << "\n\n\t" << "svn can be downloaded from:"
40 << "\n\n\t\t" << "http://subversion.tigris.org/project_packages.html\n"
43 ^this.newCopyArgs(url ? "https://quarks.svn.sourceforge.net/svnroot/quarks", local ?? {Quarks.local})
46 // returns true if some change was performed
47 checkSVNandBackupIfNeeded{
49 files = (Quarks.local.path ++ "/*").pathMatch;
50 if ( files.size != 0 ) {
51 // there are files in the quarks dir
52 if ( (Quarks.local.path ++ "/.svn").pathMatch.size == 0 ) {
53 // but quarks dir itself is not under version control
55 res = ("svn st -q " ++ it ).unixCmdGetStdOut;
57 // no local modifications, so delete the folder
58 ("rm -r " ++ it ).unixCmd;
60 // local modifications, so copy the folder for backup
61 ("mv" + it + it.drop(-1) ++ "_modified" ).unixCmd;
62 ("You had local modifications in quark folder" + it + "a copy of the folder has been made, so please review your modifications there").inform;
71 // easiest to just check out all - BUT may waste your space since the global repos is becoming bigger and bigger!
72 checkoutAll { |localRoot|
73 this.checkSVNandBackupIfNeeded;
74 this.svn("co", this.url ++ "/", localRoot.escapeChar($ ) ++ "/")
76 // checkout a specific quark (or multiple quarks - first arg can be an array).
77 // NOTE: despite the method name, this actually uses "svn up" rather than "svn co", to ensure the base checkout is the base for this subfolder.
78 // Therefore it can be used to update, equally well as to checkout, a quark.
79 checkout { | q, localRoot, sync = false |
81 var subfolders, fullCheckout, pathSoFar, skeletonCheckout, args;
83 skeletonCheckout = [];
88 subfolders = oneq.path.split($/);
90 fullCheckout = fullCheckout ++ [oneq.path.escapeChar($ )];
91 subfolders.pop; // The final entry is the folder whose entire contents we want
94 skeletonCheckout = skeletonCheckout ++ subfolders.collect{ |element, index|
95 pathSoFar = pathSoFar ++ "/" ++ element
96 }.collect{|el| el.escapeChar($ )};
99 // Now construct a svn command for the skels, and then a svn command for the fulls
100 args = if(skeletonCheckout.isEmpty){
103 ["update", ["--non-recursive"] ++ skeletonCheckout.collect(_.asSymbol).as(OrderedIdentitySet).collectAs((_.asString), Array)]
104 } ++ ["update", fullCheckout.collect(_.asSymbol).as(OrderedIdentitySet).collectAs((_.asString), Array)];
105 this.svnMulti(localRoot, sync, *args);
108 // check if the quarks directory is checked out yet
111 dir = local.path.select{|c| (c != $\\)};
112 if(File.exists(dir).not, {
113 // This method SHOULD NOT check the dir out on your behalf! That's not what it's for! Use .checkoutDirectory for that.
114 //"Quarks dir is not yet checked out. Execute:".debug;
115 //this.svn("co","--non-recursive",this.url, local.path.escapeChar($ ));
116 //this.svn("up", local.path.escapeChar($ ) +/+ "DIRECTORY");
122 // updateDirectory and checkoutDirectory can be handled by the same function, simplifying the user experience, hopefully.
123 // TODO: deprecate checkoutDirectory methods, simply use updateDirectory whether or not it's the first time.
124 // Then update the help docs to the simpler instructions.
125 checkoutDirectory {|forceSync=false|
126 ^this.updateDirectory(forceSync);
129 // DIRECTORY contains a quark spec file for each quark regardless if checked out / downloaded or not.
130 updateDirectory { |forceSync=false|
132 "\n\tSince SVN not installed, you cannot checkout Quarks. ".postln.halt;
135 // If there's no svn metadata then either there's nothing there at all or there's a non-svn thing in the way
136 if ( File.exists(local.path ++ "/.svn").not) {
137 if( File.exists(local.path).not ) {
138 // Main folder doesn't exist at all, simply check it out
139 this.svnMulti(".", forceSync,
140 "checkout", ["--non-recursive", this.url, local.path.select{|c| (c != $\\)}.escapeChar($ )],
141 // and then do the directory update:
142 "update", [local.path.select{|c| (c != $\\)}.escapeChar($ ) +/+ "DIRECTORY"]
146 << "\n\tCurrent Quarks are not SVN. Delete the directories \n\t\t "
147 << local.path << "\n\tand\n\t\t"
148 << Platform.userExtensionDir << "/quarks\n"
149 << "\tand recompile before checking out quarks";
153 this.svnMulti(local.path, forceSync, "update", ["DIRECTORY"]);
157 this.checkSVNandBackupIfNeeded;
158 if(this.checkDir.not){
159 this.checkoutDirectory; // ensures that the main folder exists
160 this.svn("update",local.path.escapeChar($ ));
162 // The "checkout" method can do the updating of individual quarks for us
163 this.checkout(local.quarks, local.path);
166 // load all specification quark objects from DIRECTORY
167 // they may or may not be locally checked out
170 paths = (local.path ++ "/DIRECTORY/*.quark").pathMatch;
171 quarks = Array(paths.size);
174 { var q=Quark.fromFile(p, this.local.parent); quarks add: q }
175 { |e| e.errorString.postln }
179 // search DIRECTORY quark objects to see if quark is in repository
180 findQuark { arg name, version;
182 matches = this.quarks.select({ |q| q.name == name });
184 matches = matches.select({ |q| q.version >= version });
186 ^matches.sort({ |a,b| a.version > b.version }).first
189 // Can perform multiple svn commands in one call.
190 // Call it with [cmd, args, cmd, args] pairs - e.g. svnMulti(.... "co", ["--quiet", "/some/repo"], "up", ["~/my/repo"]).
191 // "forceSync" is whether or not to force to run in sync (on OSX we like to do it async to avoid certificate-pain)
192 svnMulti { | baseDir, forceSync=(false) ... pairs |
193 var cmd, svnpath = this.class.svnpath.escapeChar($ );
195 Error("SVN is not installed! Quarks cannot be updated.").throw;
197 cmd = "export LANG='' && cd" + baseDir.select{|c| (c != $\\)}.escapeChar($ );
198 pairs.pairsDo{|onecmd, args|
199 cmd = cmd + "&&" + svnpath + onecmd + args.join(" ")
206 if(forceSync.not and: {thisProcess.platform.name == \osx}){
207 // asynchronous but user-friendly execution - on OSX we
208 // run it in a terminal window to minimise the risk of people getting stuck without a certificate
210 --------------------------------------------------------------
212 SuperCollider Quarks: accessing remote repository.
214 If this is the first time, you may be asked to accept a
215 security certificate. If you can trust it, please do so!
217 The command being run is:
218 " ++ cmd.escapeChar($") ++ "
220 --------------------------------------------------------------
221 " ++ $" ++ cmd).runInTerminal
223 // synchronous execution:
224 cmd.unixCmdGetStdOut.postln;
227 svn { | cmd ... args |
228 ^this.svnMulti(".", false, cmd, args);
230 // Allows to wait for command to complete
231 svnSync { | cmd ... args |
232 ^this.svnMulti(".", true, cmd, args);
236 svnp { |cmd ... args|
237 cmd = ("svn" + cmd + args.join(" "));