% Copyright (C) 2003 David Roundy
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2, or (at your option)
% any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; see the file COPYING. If not, write to
% the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
% Boston, MA 02110-1301, USA.
\darcsCommand{dist}
\begin{code}

moduleDarcs.Commands.Dist(dist)whereimportSystem.Directory(setCurrentDirectory)importWorkaround(getCurrentDirectory)importSystem.Exit(ExitCode(..),exitWith)importSystem.Cmd(system)importSystem.FilePath.Posix(takeFileName,(</>))importData.Char(isAlphaNum)importControl.Monad(when)importDarcs.Commands(DarcsCommand(DarcsCommand,commandName,commandHelp,commandDescription,commandExtraArgs,commandExtraArgHelp,commandCommand,commandPrereq,commandGetArgPossibilities,commandArgdefaults,commandAdvancedOptions,commandBasicOptions),nodefaults)importDarcs.Arguments(DarcsFlag(Verbose,DistName),distnameOption,workingRepoDir,matchOne,storeInMemory,fixSubPaths)importDarcs.Match(getNonrangeMatch,haveNonrangeMatch)importDarcs.Repository(amInRepository,withRepoReadLock,($-),--withRecorded,createPartialsPristineDirectoryTree)importDarcs.Repository.Prefs(getPrefval)importDarcs.Lock(withTemp,withTempDir,readBinFile)importDarcs.RepoPath(AbsolutePath,toFilePath)importDarcs.Utils(withCurrentDirectory)importExec(exec,Redirect(..))distDescription::StringdistDescription="Create a distribution tarball."distHelp::StringdistHelp="The `darcs dist' command creates a compressed archive (a `tarball') in\n"++"the repository's root directory, containing the recorded state of the\n"++"working tree (unrecorded changes and the _darcs directory are\n"++"excluded).\n"++"\n"++"If a predist command is set (see `darcs setpref'), that command will\n"++"be run on the tarball contents prior to archiving. For example,\n"++"autotools projects would set it to `autoconf && automake'.\n"++"\n"++"By default, the tarball (and the top-level directory within the\n"++"tarball) has the same name as the repository, but this can be\n"++"overridden with the --dist-name option.\n"-- FIXME: this is tedious and ugly.{-
++ "\n" ++
"Suppose you use a version numbering scheme `major.minor.patch', and\n" ++
"you tag each release `major.minor'. You can then calculate the\n" ++
"version number by taking the newest tag and appending a dot and the\n" ++
"number of patches since that tag. If you use the directory name as\n" ++
"the project name, you can make tarballs of the form name-version.tgz\n" ++
"using the following shell script:\n" ++
"\n" ++
" major_minor=$(darcs show tags | head -1) &&\n" ++
" patch_level=$(($(darcs changes --count --from-tag .) - 1)) &&\n" ++
" version=$major_minor.$patch_level &&\n" ++
" project=${PWD##*/} &&\n" ++
" darcs dist --dist-name \"$project\"-\"$version\".tar.gz\n"
-}dist::DarcsCommanddist=DarcsCommand{commandName="dist",commandHelp=distHelp,commandDescription=distDescription,commandExtraArgs=0,commandExtraArgHelp=[],commandCommand=distCmd,commandPrereq=amInRepository,commandGetArgPossibilities=return[],commandArgdefaults=nodefaults,commandAdvancedOptions=[],commandBasicOptions=[distnameOption,workingRepoDir,matchOne,storeInMemory]}distCmd::[DarcsFlag]->[String]->IO()distCmdoptsargs=withRepoReadLockopts$-\repository->dodistname<-getDistNameoptsverb<-return$Verbose`elem`optspredist<-getPrefval"predist"formerdir<-getCurrentDirectorypath_list<-ifnullargsthenreturn[""]elsemaptoFilePath`fmap`fixSubPathsoptsargsresultfile<-return(formerdir</>distname++".tar.gz")withTemp$\tarfile->withTempDir"darcsdist"$\tempdir->dosetCurrentDirectory(formerdir)withTempDir(toFilePathtempdir</>takeFileNamedistname)$\ddir->doifhaveNonrangeMatchoptsthenwithCurrentDirectoryddir$getNonrangeMatchrepositoryoptselsecreatePartialsPristineDirectoryTreerepositorypath_list(toFilePathddir)ec<-casepredistofNothing->returnExitSuccessJustpd->systempdif(ec==ExitSuccess)thendoDistverbtarfiletempdirddirresultfileelsedoputStrLn"Dist aborted due to predist failure"exitWithec-- | This function performs the actual distribution action itself.-- NB - it does /not/ perform the pre-dist, that should already-- have completed successfully before this is invoked.doDist::Bool->FilePath->AbsolutePath->AbsolutePath->FilePath->IO()doDistverbtarfiletempdirddirresultfile=dosetCurrentDirectory(toFilePathtempdir)exec"tar"["-cf","-",safename$takeFileName$toFilePathddir](Null,Filetarfile,AsIs)whenverb$withTemp$\tar_listing->doexec"tar"["-tf","-"](Filetarfile,Filetar_listing,Stdout)to<-readBinFiletar_listingputStrtoexec"gzip"["-c"](Filetarfile,Fileresultfile,AsIs)putStrLn$"Created dist as "++resultfilewheresafenamen@(c:_)|isAlphaNumc=nsafenamen="./"++nguessRepoName::IOStringguessRepoName=dopwd<-getCurrentDirectoryif'/'`elem`pwdthenreturn$reverse$takeWhile(/='/')$reversepwdelsereturn"cantguessreponame"getDistName::[DarcsFlag]->IOStringgetDistName(DistNamedn:_)=returndngetDistName(_:fs)=getDistNamefsgetDistName_=guessRepoName