This commit is contained in:
Andrey Kotlarski 2012-05-07 00:49:11 +03:00
commit d065b30eaa
88 changed files with 809 additions and 293 deletions

367
README.md
View file

@ -6,8 +6,7 @@ code using simple recipes. (Think of it as a server-side version of
[el-get](https://github.com/dimitri/el-get), or even [el-get](https://github.com/dimitri/el-get), or even
[homebrew](https://github.com/mxcl/homebrew).) [homebrew](https://github.com/mxcl/homebrew).)
Packages are updated when changes are made to the MELPA repository, Packages are updated hourly.
or at least daily.
If you just want to browse and install packages, check out the If you just want to browse and install packages, check out the
[archive index page](http://melpa.milkbox.net/) for instructions. [archive index page](http://melpa.milkbox.net/) for instructions.
@ -15,85 +14,115 @@ If you just want to browse and install packages, check out the
Adding packages is as simple as submitting a pull request; read on for Adding packages is as simple as submitting a pull request; read on for
details. details.
### About the name ## Table of Contents
*MELPA* is *Milkypostman's ELPA* or *Milkypostman's Experimental Lisp * [Usage](#usage)
Package Archive* if you're not into the whole brevity thing. * [Contributing](#contributing-new-packages)
* [Package Format](#package-format)
* [Server Scripts](#server-scripts)
* [API](#api)
* [MELPA Package](#melpa-package)
* [About](#about)
## Scripts
* `buildpkg` -- Create an archive of the package(s) passed as ## Usage
arguments to the script. Built packages are put in the `packages/`
folder with version corresponding to the newest HEAD revision
available; given according to the `%Y%m%d` format.
* `melpa` -- All the logic for generating everything in the repository To use the MELPA repository, add it to `package-archives` before the
based on the recipe files. By default this will clean the `packages/` directory, call to `package-initialize` in your `init.el` file.
build all packages
listed under `recipes/`, and compile the `index.html` file for the [melpa]
website front page.
The following arguments are accepted: (add-to-list 'package-archives
'("melpa" . "http://melpa.milkbox.net/packages/") t)
Since `package.el` doesn't allow locking packages to certain version,
we also provide a package `melpa.el` which contains code to allow
restricting packages to specific repositories. This allows someone to
blacklist packages that come from a specific repository, or blacklist
all packages from a repository and only whitelist certain packages.
clear See the [MELPA Package](#melpa-package) section below or
: clean out the `packages/` directory [Installing](http://melpa.milkbox.net/#installing) section on the
MELPA homepage.
build
: build all packages in `pkglist`
index
: build the `index.html` file
validate
: naively validate that the correct number of packages were built.
Note that these scripts require an Emacs with `package.el` installed,
such as Emacs 24. If you have an older version of Emacs, you can get a
suitable `package.el` [here](http://bit.ly/pkg-el23).
[melpa]: http://melpa.milkbox.net
## Code
The `package-build.el` file contains all the heavy lifting. The
scripts above call the `package-build-archive` function from the
command-line to actually build the package(s).
Use `(package-build-all)` to build all melpa packages.
Alternatively you can
load this file from within Emacs and issues commands from there.
The `package-build.el` automatically generates any required
information for the package. For multi-file packages this include
generating the file `<NAME>-pkg.el` which contains *description*,
*version*, and *requires* information determined by searching
`<NAME>-pkg.el`, `<NAME>.el`, and `<NAME>-pkg.el.in` if they exist in
the repository.
## Contributing New Packages ## Contributing New Packages
For submitting new packages we ask you following the following
guidelines,
* Upstream source must be stored in an authoritative
[SCM](http://en.wikipedia.org/wiki/Software_configuration_management)
repository or on the Emacswiki.
<!-- * Package must be actively developed and not otherwise included in a -->
<!-- different ELPA archive. Packages that are better suited for -->
<!-- [marmalade](http://marmalade-repo.org/) -->
* Submit one pull request per recipe. You can create multiple
branches and create a pull request for each branch.
* Recipes should try to minimize the size of the resulting package by
specifying only files relevant to the package. See the
[Package Format](#package-format) section for more information on
specifying package files.
* The package name should match the name of the feature provided. See
the `package` function for more information.
* Packages should adhere to the `package.el` format as specified by
`(info "(elisp) Packaging")`. More information on this format is
provided by the
[marmalade package manual](http://marmalade-repo.org/doc-files/package.5.html).
### Testing
Let `<NAME>` denote the name of the recipe to submit.
1. Fork the MELPA repository.
2. Add your new file under the directory specified by
`package-build-recipes-dir` (default: `recipes/` directory where
`package-build` was loaded).
3. Confirm your package build properly by running
./buildpkg <NAME>
4. Install the file you built by running `package-install-file` from
within Emacs and specifying the newly built package in the directory
specified by `package-build-archive-dir` (default: `packages/`
directory where `package-build` was loaded).
### Submitting
After verifying the entry works properly please open a pull request on
Github. Consider the [hub](https://github.com/defunkt/hub)
command-line utility by [defunkt](http://chriswanstrath.com/) which
helps simplify this process.
## Package Format
Packages are specified by files in the `recipes` directory. You can Packages are specified by files in the `recipes` directory. You can
contribute a new package by adding a new file under `recipes` using contribute a new package by adding a new file under `recipes` using
the following form, the following form,
```elisp ```elisp
(name (<package-name>
:fetcher [git|github|bzr|hg|darcs|svn|wiki] :fetcher [git|github|bzr|hg|darcs|svn|wiki]
[:url "<repo url>"] [:url "<repo url>"]
[:repo "github-user/repo-name"] [:repo "github-user/repo-name"]
[:files ("<file1>", ...)]) [:files ("<file1>", ...)])
``` ```
`name` - `package-name`
: a lisp symbol that has the same name as the package being specified. a lisp symbol that has the same name as the package being specified.
`:url` - `:url`
: specifies the URL of the version control repository. *required for specifies the URL of the version control repository. *required for
the `git`, `bzr`, `hg`, `darcs` and `svn` fetchers* the `git`, `bzr`, `hg`, `darcs` and `svn` fetchers*
`:fetcher` - `:fetcher`
: specifies the type of repository that `:url` points to. Right now specifies the type of repository that `:url` points to. Right now
package-build supports [git][git], [github][github], package-build supports [git][git], [github][github],
[bazaar (bzr)][bzr], [mercurial (hg)][hg], [bazaar (bzr)][bzr], [mercurial (hg)][hg],
[subversion (svn)][svn], [darcs][darcs], and [subversion (svn)][svn], [darcs][darcs], and
@ -109,12 +138,13 @@ differs from the package name being built. In the case of the `github`
fetcher, use `:repo` instead of `:url`; the git URL will then be fetcher, use `:repo` instead of `:url`; the git URL will then be
deduced. deduced.
`:files` - `:files`
: optional property specifying the explicit files used to build the optional property specifying the explicit files used to build the
package. Automatically populated by matching all `.el` files in the package. Automatically populated by matching all `.el` files in the
root of the repository. This is necessary when there are multiple root of the repository. This is necessary when there are multiple
`.el` files in the repository but the package should only be built `.el` files in the repository but the package should only be built
from a subset. from a subset. *Any file in any path in the repository is copied to
the root of the package*
[git]: http://git-scm.com/ [git]: http://git-scm.com/
[github]: https://github.com/ [github]: https://github.com/
@ -163,16 +193,157 @@ directory of the repository. The `starter-kit-bindings` repository is
contained in the `modules/` subdirectory and thus needs the packages contained in the `modules/` subdirectory and thus needs the packages
files specified explicitly. files specified explicitly.
### Submitting the Package
You should first fork the MELPA repository, add your new file under ### Multiple Files in Multiple Directories
`recipes`, and confirm your new package builds properly by running
`buildpkg <NAME>`. You can install the package that you built by There are special cases when we need
running the interactive command `package-install-file` in Emacs, and There are special cases where creation of the package comes from many
specifying the newly built package which should be in the `packages/` different sub-directories in the repository and the destination
subdirectory under the melpa directory. sub-directories need to be explicitly set.
Consider the `flymake-perlcritic` recipe,
```elisp
(flymake-perlcritic :repo "illusori/emacs-flymake-perlcritic"
:fetcher github
:files ("*.el" ("bin" "bin/flymake_perlcritic")))
```
which will result in a package structure of,
```
flymake-perlcritic-YYYMMDD
|-- bin
| `-- flymake_perlcritic
|-- flymake-perlcritic-pkg.el
`-- flymake-perlcritic.el
```
Notice that specifying an entry in `:files` that is a list takes the
first element to be the destination directory. These can be embedded
further, such as the following---hypothetical---entry for `:files`,
```elisp
("*.el" ("snippets"
("html-mode" "snippets/html-mode/*")
("python-mode" "snippets/python-mode/*")))
```
which would result in a package with `*.el` in something like,
```
package-YYYYMMDD
|-- snippets
| |-- html-mode
| | |-- div
| | `-- html
| `-- python-mode
| |-- for
| `-- main
`-- package.el
```
But a better solution, given that we probably want to copy the
*entire* `snippets` directory to the root of the package, we could
just specify that directory. Consider the `pony-mode` recipe,
```elisp
(pony-mode
:repo "davidmiller/pony-mode"
:fetcher github
:files ("src/*.el" "snippets"))
```
which generates the package,
```
pony-mode-YYYYMMDD
|-- pony-mode-pkg.el
|-- pony-mode.el
|-- pony-tpl.el
`-- snippets
|-- html-mode
| |-- bl
| |-- ex
| |-- for
| |-- if
| |-- loa
| |-- sup
| |-- testc
| `-- {{
`-- python-mode
|-- auth-view
|-- bn
|-- model
|-- modelform
|-- render-to
|-- testc
`-- view
```
## Build Scripts
The scripts described here
* `buildpkg` -- Create an archive of the package(s) passed as
arguments to the script. Built packages are put in the `packages/`
folder with version corresponding to the newest HEAD revision
available; given according to the `%Y%m%d` format.
* `melpa` -- All the logic for generating everything in the repository
based on the recipe files. By default build all packages listed under
`recipes/`, and compile the `index.html` file for the [melpa] website.
The following arguments are accepted:
clear : clean out the `packages/` directory
build : build all packages in `pkglist`
index : build the `index.html` file
validate :naively validate that the correct number of packages were built.
Note that these scripts require an Emacs with `package.el` installed,
such as Emacs 24. If you have an older version of Emacs, you can get a
suitable `package.el` [here](http://bit.ly/pkg-el23).
[melpa]: http://melpa.milkbox.net
## API
All repository code is contained in the `package-build.el`.
### Functions
- `(package-build-all)` : build packages for all recipes in the
directory specified by `package-build-recipes-dir`.
- `(package-build-archive NAME)` : interactive elisp function to build
a single archive. NAME is a symbol for the package to be built.
Packages are staged in the directory specified by
`package-build-working-dir` and built packages are placed in the
directory specified by `package-build-archive-dir`. Packages are
versioned based on the most recent commit date to package files based
on commits to upstream package repository. For multi-file packages,
the file `<NAME>-pkg.el` is automatically generated and contains
*description*, *version*, and *requires* information determined by
searching `<NAME>-pkg.el`, `<NAME>.el`, and `<NAME>-pkg.el.in`, if
they exist in the repository.
### Variables
- `package-build-working-dir` : Staging area containing package
repositories and package directories being built.
- `package-build-archive-dir` : Location to store `archive-contents` and
any built packages.
- `package-build-recipes-dir` : Directory containing MELPA compatible
recipes. See [Package Format](#package-format) section for more details.
After verifying the entry works properly please open a pull request on Github.
## Configuration ## Configuration
@ -181,3 +352,61 @@ This can be configured using the `package-build-archive-dir` variable.
Repositories are checked out to the `working/` directory by default. Repositories are checked out to the `working/` directory by default.
This can be configured using the `package-build-working-dir` variable. This can be configured using the `package-build-working-dir` variable.
## MELPA Package
The `melpa.el` package---available in MELPA--allows creating a
whitelist or blacklist of packages for a specific repository. This
allows for disabling all packages from a specific repository and only
enabling certain packages, or simply blacklist a certain subset of packages.
### Configuring
By default there are two variables that can be customized to specify
which packages will be enabled (whitelist packages only) or excluded
(blacklist of packages)
- `package-archive-enable-alist` : Optional Alist of enabled packages
used by `package-filter`. The format is (ARCHIVE . PACKAGE ...),
where ARCHIVE is a string matching an archive name in
`package-archives`, PACKAGE is a symbol of a package in ARCHIVE to
enable. If no ARCHIVE exists in the alist, all packages are
enabled.
If no ARCHIVE exists in the alist, all packages are enabled.
<!-- extra padding??? -->
- `package-archive-exclude-alist` : Alist of packages excluded by
`package-filter`. The format is (ARCHIVE . PACKAGE ...), where
ARCHIVE is a string matching an archive name in
`package-archives`, PACKAGE is a symbol of a package in that
archive to exclude. Any specified package is excluded regardless
of the value of `package-archive-enable-alist`
If a particular ARCHIVE has an entry in
`package-archive-enable-alist` then only packages
### Manual Installation
You can install the package manually by pasting this into yoru `*scratch*` buffer and evaluating it.
(progn
(switch-to-buffer
(url-retrieve-synchronously
"https://raw.github.com/milkypostman/melpa/master/melpa.el"))
(package-install-from-buffer (package-buffer-info) 'single))
## About
*MELPA* is *Milkypostman's ELPA* or *Milkypostman's Experimental Lisp
Package Archive* if you're not into the whole brevity thing.

View file

@ -4,4 +4,4 @@
(require 'package-build) (require 'package-build)
(mapc 'package-build-archive argv) (mapc 'package-build-archive (mapcar 'intern argv))

View file

@ -1,23 +1,21 @@
%% MELPA %% MELPA
*MELPA* or *Milkypostman's ELPA* or *Milkypostman's Experimental Lisp Package Repository* if you're not into the whole brevity thing.
[ [Packages](#current-list-of-packages) ] [ [Packages](#current-list-of-packages) ]
[ [Installing](#installing) ] [ [Installing](#installing) ]
[ [Known Issues](#known-issues) ] [ [Known Issues](#known-issues) ]
[ [Updating Packages](#updating-packages) ] [ [Updating Packages](#updating-packages) ]
[ [Development](#development) ] [ [Development](#development) ]
*MELPA* or *Milkypostman's ELPA* or *Milkypostman's Experimental Lisp Package Repository* if you're not into the whole brevity thing. **Last Update:** *<%= Time.now.strftime("%Y.%m.%d %H:%M %z") %>*
> a repository for development versions of Emacs packages (hot from the repo).
**Last Update:** *<%= Time.now.strftime("%Y.%m.%d %H:%M") %>*
## Current List of Packages ## Current List of Packages
<% <%
def parse str def parse str
# credit to: http://stackoverflow.com/q/3128406/154508 # credit to: http://stackoverflow.com/q/3128406/154508
tokens = str.scan(/#{Regexp.escape("(")}|#{Regexp.escape(")")}|[a-zA-Z0-9\'\-\_\+]+/) tokens = str.scan(/#{Regexp.escape("(")}|#{Regexp.escape(")")}|"(?:\\.|[^"])+"|[a-zA-Z0-9\'\-\_\+]+/)
stack = [[]] stack = [[]]
@ -27,6 +25,8 @@
stack << [] stack << []
when ")" when ")"
stack[-2] << stack.pop stack[-2] << stack.pop
when /^"(.*)\"$/
stack[-1] << $1
else else
stack[-1] << tok stack[-1] << tok
end end
@ -36,17 +36,24 @@
end end
headers = ["Package", "Version", "Description"] headers = ["Package", "Version", "Description", "Source"]
data = parse(File.open("../packages/archive-contents").read)[1..-1] data = parse(File.open("../packages/archive-contents").read)[1..-1]
data.map! do |row| data.map! do |row|
[row[0], row[1][0], row[3..-2].join(" ")] pkgname = row[0]
pkgurl = "packages/#{row[0]}-#{row[1]}." + (row[-1] == "single" ? "el" : "tar")
recipe_url = "https://github.com/milkypostman/melpa/blob/master/recipes/#{pkgname}"
descr, source = row[3..-2].join(" "), "other"
if descr =~ /(.*?)(\s*-\*-.*?)?\s*\[source:\s*(\w+)\]\s*/
descr, source = $1, $3
end
["[#{pkgname}](#{pkgurl})", row[1][0], descr, "[#{source}](#{recipe_url})"]
end end
data.sort! data.sort!
colwidth = [0,0,0] colwidth = [0,0,0, 9]
data.map do |row| data.map do |row|
row.to_enum(:each_with_index).map do |e,i| row.to_enum(:each_with_index).map do |e,i|

View file

@ -53,7 +53,9 @@ pre code {
pre { pre {
background: #000; background: #000;
color: #fff; color: #fff;
padding: 15px;} padding: 15px;
}
hr { hr {
border: 0; border: 0;
width: 80%; width: 80%;
@ -78,6 +80,14 @@ td, th {
padding: 2px 7px 2px 7px; padding: 2px 7px 2px 7px;
} }
tr {
color: #ddd;
}
td:first-child a {
color: #fff;
}
tr.header { tr.header {
color: #000; color: #000;
background-color: #fad; background-color: #fad;

17
melpa
View file

@ -7,6 +7,7 @@ cd ${BASEDIR} || exit 1
function melpa_clear_packages { function melpa_clear_packages {
echo "*** Clearing the packages folder..." echo "*** Clearing the packages folder..."
date
rm -rf packages/* rm -rf packages/*
echo echo
} }
@ -14,12 +15,14 @@ function melpa_clear_packages {
function melpa_build_pkglist { function melpa_build_pkglist {
echo "*** Building all packages..." echo "*** Building all packages..."
date
emacs --batch -l package-build.el --eval "(package-build-all)" emacs --batch -l package-build.el --eval "(package-build-all)"
echo echo
} }
function melpa_generate_html { function melpa_generate_html {
echo "*** Building html" echo "*** Building html..."
date
cd html || return 1 cd html || return 1
erb index.erb > index.md erb index.erb > index.md
pandoc --template=template.html --css=style.css -s --mathml -t html --smart index.md > index.html pandoc --template=template.html --css=style.css -s --mathml -t html --smart index.md > index.html
@ -31,15 +34,8 @@ function trim {
echo $1 echo $1
} }
function melpa_validate {
NUMPACKAGES=$(trim `ls recipes/* | wc -l`)
NUMBUILT=$(trim `ls packages/*.{el,tar} | wc -l`)
echo "${NUMBUILT}/${NUMPACKAGES} packages built"
}
function print_usage { function print_usage {
echo "usage: $0 [-h | -?] [clear | build | html | validate]" echo "usage: $0 [-h | -?] [clear | build | html ]"
} }
args=`getopt h $*` args=`getopt h $*`
@ -66,7 +62,7 @@ done
if [[ "$#" == "0" ]]; then if [[ "$#" == "0" ]]; then
set -- clear build index validate set -- build index
fi fi
for i; do for i; do
@ -74,7 +70,6 @@ for i; do
clear ) melpa_clear_packages ;; clear ) melpa_clear_packages ;;
build ) melpa_build_pkglist ;; build ) melpa_build_pkglist ;;
html | index ) melpa_generate_html ;; html | index ) melpa_generate_html ;;
validate ) melpa_validate ;;
esac esac
shift shift
done done

27
missing.el Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env emacs --script
(defun difference (left right)
"compare two lists"
(let ((caleft (car left))
(caright (car right)))
(cond
((not left) right)
((not right) left)
((string< caleft caright)
(cons caleft (difference (cdr left) right)))
((string< caright caleft)
(cons caright (difference left (cdr right))))
(t (difference (cdr left) (cdr right))))))
(defun stripstuff (fn)
"strip the date and extension"
(string-match "\\\(.*\\\)-[0-9]+\.\\\(el$\\\|tar$\\\)" fn)
(match-string 1 fn))
(mapc 'message
(difference
(sort (directory-files "recipes/" nil "[^.].*") 'string<)
(sort (mapcar 'stripstuff (directory-files "packages/" nil "[^.].*\\\(el$\\\|tar$\\\)")) 'string<)))

View file

@ -1,6 +1,7 @@
;;; package-build.el --- Tools for curating the package archive ;;; package-build.el --- Tools for curating the package archive
;; Copyright (C) 2011 Donald Ephraim Curtis <dcurtis@milkbox.net> ;; Copyright (C) 2011-2012 Donald Ephraim Curtis <dcurtis@milkbox.net>
;; Copyright (C) 2012 Steve Purcell <steve@sanityinc.com>
;; Copyright (C) 2009 Phil Hagelberg <technomancy@gmail.com> ;; Copyright (C) 2009 Phil Hagelberg <technomancy@gmail.com>
;; Author: Donald Ephraim Curtis <dcurtis@milkbox.net> ;; Author: Donald Ephraim Curtis <dcurtis@milkbox.net>
@ -8,11 +9,6 @@
;; Version: 0.1 ;; Version: 0.1
;; Keywords: tools ;; Keywords: tools
;;
;; Credits:
;; Steve Purcell
;;
;; This file is not (yet) part of GNU Emacs. ;; This file is not (yet) part of GNU Emacs.
;; However, it is distributed under the same license. ;; However, it is distributed under the same license.
@ -36,7 +32,7 @@
;; This file allows a curator to publish an archive of Emacs packages. ;; This file allows a curator to publish an archive of Emacs packages.
;; The archive is generated from an index, which contains a list of ;; The archive is generated from an index, which contains a list of
;; projects and repositories from which to get them. The term ;; projects and repositories from which to get them. The term
;; "package" here is used to mean a specific version of a project that ;; "package" here is used to mean a specific version of a project that
;; is prepared for download and installation. ;; is prepared for download and installation.
@ -71,13 +67,35 @@
;;; Internal functions ;;; Internal functions
(defun pb/find-parse-time (regex) (defun pb/parse-time (str)
"Find REGEX in current buffer and format as a proper time version." "Parse STR as a time, and format as a YYYYMMDD string."
(message (format "%s\n" (substring-no-properties str)))
(format-time-string (format-time-string
"%Y%m%d" "%Y%m%d"
(date-to-time (date-to-time
(print (progn (re-search-backward regex) (substring-no-properties str))))
(match-string-no-properties 1))))))
(defun pb/string-match-all (regex str &optional group)
"Find every match for `REGEX' within `STR', returning the full match string or group `GROUP'."
(let (result
(pos 0)
(group (or group 0)))
(while (string-match regex str pos)
(push (match-string group str) result)
(setq pos (match-end group)))
result))
(defun pb/find-parse-time (regex &optional bound)
"Find REGEX in current buffer and format as a time version, optionally looking only as far as BOUND."
(pb/parse-time (progn (re-search-backward regex bound)
(match-string-no-properties 1))))
(defun pb/find-parse-time-latest (regex &optional bound)
"Find the latest timestamp matching REGEX, optionally looking only as far as BOUND."
(let* ((text (buffer-substring-no-properties
(or bound (point-min)) (point)))
(times (mapcar 'pb/parse-time (pb/string-match-all regex text 1))))
(car (nreverse (sort times 'string<)))))
(defun pb/run-process (dir prog &rest args) (defun pb/run-process (dir prog &rest args)
"In DIR (or `default-directory' if unset) run command PROG with ARGS. "In DIR (or `default-directory' if unset) run command PROG with ARGS.
@ -88,6 +106,14 @@ Output is written to the current buffer."
(error "Program '%s' with args '%s' exited with non-zero status %d" (error "Program '%s' with args '%s' exited with non-zero status %d"
prog args exit-code))))) prog args exit-code)))))
(defun pb/run-process-match (regex dir prog &rest args)
"Find match for REGEX when - in DIR, or `default-directory' if unset - we run PROG with ARGS."
(with-temp-buffer
(apply 'pb/run-process dir prog args)
(goto-char (point-min))
(re-search-forward regex)
(match-string-no-properties 1)))
(defun pb/checkout (name config cwd) (defun pb/checkout (name config cwd)
"Check out source for package NAME with CONFIG under working dir CWD. "Check out source for package NAME with CONFIG under working dir CWD.
@ -95,7 +121,10 @@ In turn, this function uses the :fetcher option in the config to
choose a source-specific fetcher function, which it calls with choose a source-specific fetcher function, which it calls with
the same arguments." the same arguments."
(let ((repo-type (plist-get config :fetcher))) (let ((repo-type (plist-get config :fetcher)))
(print repo-type) (message (format "%s " repo-type))
(unless (eq 'wiki repo-type)
(message (format "%s\n"
(or (plist-get config :repo) (plist-get config :url)))))
(funcall (intern (format "pb/checkout-%s" repo-type)) (funcall (intern (format "pb/checkout-%s" repo-type))
name config cwd))) name config cwd)))
@ -123,19 +152,23 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
(defun pb/grab-wiki-file (filename) (defun pb/grab-wiki-file (filename)
"Download FILENAME from emacswiki, returning its last-modified time." "Download FILENAME from emacswiki, returning its last-modified time."
(let* ((download-url (format "http://www.emacswiki.org/emacs/download/%s" filename)) (let* ((download-url
(wiki-url (format "http://www.emacswiki.org/emacs/%s" filename))) (format "http://www.emacswiki.org/emacs/download/%s" filename))
(wiki-url
(format "http://www.emacswiki.org/emacs/%s" filename)))
(pb/with-wiki-rate-limit (pb/with-wiki-rate-limit
(url-copy-file download-url filename t)) (url-copy-file download-url filename t))
(when (zerop (nth 7 (file-attributes filename)))
(error "Wiki file %s was empty - has it been removed?" filename))
(with-current-buffer (pb/with-wiki-rate-limit (with-current-buffer (pb/with-wiki-rate-limit
(url-retrieve-synchronously wiki-url)) (url-retrieve-synchronously wiki-url))
(message (format "%s\n" download-url))
(pb/find-parse-time (pb/find-parse-time
"Last edited \\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\} [A-Z]\\{3\\}\\)")))) "Last edited \\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\} [A-Z]\\{3\\}\\)"))))
(defun pb/checkout-wiki (name config dir) (defun pb/checkout-wiki (name config dir)
"Checkout package NAME with config CONFIG from the EmacsWiki into DIR." "Checkout package NAME with config CONFIG from the EmacsWiki into DIR."
(with-current-buffer (get-buffer-create "*package-build-checkout*") (with-current-buffer (get-buffer-create "*package-build-checkout*")
(message dir)
(unless (file-exists-p dir) (unless (file-exists-p dir)
(make-directory dir)) (make-directory dir))
(let ((files (or (plist-get config :files) (let ((files (or (plist-get config :files)
@ -145,11 +178,7 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
(defun pb/darcs-repo (dir) (defun pb/darcs-repo (dir)
"Get the current darcs repo for DIR." "Get the current darcs repo for DIR."
(with-temp-buffer (pb/run-process-match "Default Remote: \\(.*\\)" dir "darcs" "show" "repo"))
(pb/run-process dir "darcs" "show" "repo")
(goto-char (point-min))
(re-search-forward "Default Remote: \\(.*\\)")
(match-string-no-properties 1)))
(defun pb/checkout-darcs (name config dir) (defun pb/checkout-darcs (name config dir)
"Check package NAME with config CONFIG out of darcs into DIR." "Check package NAME with config CONFIG out of darcs into DIR."
@ -158,51 +187,60 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
(cond (cond
((and (file-exists-p (expand-file-name "_darcs" dir)) ((and (file-exists-p (expand-file-name "_darcs" dir))
(string-equal (pb/darcs-repo dir) repo)) (string-equal (pb/darcs-repo dir) repo))
(print "checkout directory exists, updating...") (pb/princ-exists dir)
(pb/run-process dir "darcs" "pull")) (pb/run-process dir "darcs" "pull"))
(t (t
(when (file-exists-p dir) (when (file-exists-p dir)
(delete-directory dir t nil)) (delete-directory dir t nil))
(print "cloning repository") (pb/princ-checkout repo dir)
(pb/run-process nil "darcs" "get" repo dir))) (pb/run-process nil "darcs" "get" repo dir)))
(pb/run-process dir "darcs" "changes" "--last" "1") (apply 'pb/run-process dir "darcs" "changes" "--max-count" "1"
(pb/expand-source-file-list dir config))
(pb/find-parse-time (pb/find-parse-time
"\\([a-zA-Z]\\{3\\} [a-zA-Z]\\{3\\} \\( \\|[0-9]\\)[0-9] [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\} [A-Za-z]\\{3\\} [0-9]\\{4\\}\\)")))) "\\([a-zA-Z]\\{3\\} [a-zA-Z]\\{3\\} \\( \\|[0-9]\\)[0-9] [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\} [A-Za-z]\\{3\\} [0-9]\\{4\\}\\)"))))
(defun pb/svn-repo (dir) (defun pb/svn-repo (dir)
"Get the current svn repo for DIR." "Get the current svn repo for DIR."
(with-temp-buffer (pb/run-process-match "URL: \\(.*\\)" dir "svn" "info"))
(pb/run-process dir "svn" "info")
(goto-char (point-min)) (defun pb/trim (str &optional chr)
(re-search-forward "URL: \\(.*\\)") "Return a copy of STR without any trailing CHR (or space if unspecified)."
(match-string-no-properties 1))) (if (equal (elt str (1- (length str))) (or chr ? ))
(substring str 0 (1- (length str)))
str))
(defun pb/princ-exists (dir)
"Print a message that the contents of DIR will be updated."
(message (format "updating %s\n" dir)))
(defun pb/princ-checkout (repo dir)
"Print a message that REPO will be checked out into DIR."
(message (format "cloning %s to %s\n" repo dir)))
(defun pb/checkout-svn (name config dir) (defun pb/checkout-svn (name config dir)
"Check package NAME with config CONFIG out of svn into DIR." "Check package NAME with config CONFIG out of svn into DIR."
(let ((repo (plist-get config :url))) (with-current-buffer (get-buffer-create "*package-build-checkout*")
(with-current-buffer (get-buffer-create "*package-build-checkout*") (let ((repo (pb/trim (plist-get config :url) ?/))
(goto-char (point-max)) (bound (goto-char (point-max))))
(cond (cond
((and (file-exists-p (expand-file-name ".svn" dir)) ((and (file-exists-p (expand-file-name ".svn" dir))
(string-equal (pb/svn-repo dir) repo)) (string-equal (pb/svn-repo dir) repo))
(print "checkout directory exists, updating...") (pb/princ-exists dir)
(pb/run-process dir "svn" "up")) (pb/run-process dir "svn" "up"))
(t (t
(when (file-exists-p dir) (when (file-exists-p dir)
(delete-directory dir t nil)) (delete-directory dir t nil))
(print "cloning repository") (pb/princ-checkout repo dir)
(pb/run-process nil "svn" "checkout" repo dir))) (pb/run-process nil "svn" "checkout" repo dir)))
(pb/run-process dir "svn" "info") (apply 'pb/run-process dir "svn" "info"
(pb/find-parse-time (pb/expand-source-file-list dir config))
"\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)")))) (or (pb/find-parse-time-latest "Last Changed Date: \\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)" bound)
(error "No valid timestamps found!")))))
(defun pb/git-repo (dir) (defun pb/git-repo (dir)
"Get the current git repo for DIR." "Get the current git repo for DIR."
(with-temp-buffer (pb/run-process-match
(pb/run-process dir "git" "remote" "show" "origin") "Fetch URL: \\(.*\\)" dir "git" "remote" "show" "-n" "origin"))
(goto-char (point-min))
(re-search-forward "Fetch URL: \\(.*\\)")
(match-string-no-properties 1)))
(defun pb/checkout-git (name config dir) (defun pb/checkout-git (name config dir)
"Check package NAME with config CONFIG out of git into DIR." "Check package NAME with config CONFIG out of git into DIR."
@ -213,16 +251,17 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
(cond (cond
((and (file-exists-p (expand-file-name ".git" dir)) ((and (file-exists-p (expand-file-name ".git" dir))
(string-equal (pb/git-repo dir) repo)) (string-equal (pb/git-repo dir) repo))
(print "checkout directory exists, updating...") (pb/princ-exists dir)
(pb/run-process dir "git" "pull")) (pb/run-process dir "git" "pull"))
(t (t
(when (file-exists-p dir) (when (file-exists-p dir)
(delete-directory dir t nil)) (delete-directory dir t nil))
(print (format "cloning %s to %s" repo dir)) (pb/princ-checkout repo dir)
(pb/run-process nil "git" "clone" repo dir))) (pb/run-process nil "git" "clone" repo dir)))
(when commit (when commit
(pb/run-process dir "git" "checkout" commit)) (pb/run-process dir "git" "checkout" commit))
(pb/run-process dir "git" "show" "-s" "--format='\%ci'" "HEAD") (apply 'pb/run-process dir "git" "log" "-n1" "--pretty=format:'\%ci'"
(pb/expand-source-file-list dir config))
(pb/find-parse-time (pb/find-parse-time
"\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)")))) "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)"))))
@ -231,40 +270,37 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
(let* ((url (format "git://github.com/%s.git" (plist-get config :repo)))) (let* ((url (format "git://github.com/%s.git" (plist-get config :repo))))
(pb/checkout-git name (plist-put (copy-sequence config) :url url) dir))) (pb/checkout-git name (plist-put (copy-sequence config) :url url) dir)))
(defun pb/bzr-expand-repo (repo)
"Get REPO expanded name."
(pb/run-process-match "\\(?:branch root\\|repository branch\\): \\(.*\\)" nil "bzr" "info" repo))
(defun pb/bzr-repo (dir) (defun pb/bzr-repo (dir)
"Get the current bzr repo for DIR." "Get the current bzr repo for DIR."
(with-temp-buffer (pb/run-process-match "parent branch: \\(.*\\)" dir "bzr" "info"))
(pb/run-process dir "bzr" "info")
(goto-char (point-min))
(re-search-forward "parent branch: \\(.*\\)")
(match-string-no-properties 1)))
(defun pb/checkout-bzr (name config dir) (defun pb/checkout-bzr (name config dir)
"Check package NAME with config CONFIG out of bzr into DIR." "Check package NAME with config CONFIG out of bzr into DIR."
(let ((repo (plist-get config :url))) (let ((repo (pb/bzr-expand-repo (plist-get config :url))))
(with-current-buffer (get-buffer-create "*package-build-checkout*") (with-current-buffer (get-buffer-create "*package-build-checkout*")
(goto-char (point-max)) (goto-char (point-max))
(cond (cond
((and (file-exists-p (expand-file-name ".bzr" dir)) ((and (file-exists-p (expand-file-name ".bzr" dir))
(string-equal (pb/bzr-repo dir) repo)) (string-equal (pb/bzr-repo dir) repo))
(print "checkout directory exists, updating...") (pb/princ-exists dir)
(pb/run-process dir "bzr" "merge")) (pb/run-process dir "bzr" "merge"))
(t (t
(when (file-exists-p dir) (when (file-exists-p dir)
(delete-directory dir t nil)) (delete-directory dir t nil))
(print "cloning repository") (pb/princ-checkout repo dir)
(pb/run-process nil "bzr" "branch" repo dir))) (pb/run-process nil "bzr" "branch" repo dir)))
(pb/run-process dir "bzr" "info" "-vv") (apply 'pb/run-process dir "bzr" "log" "-l1"
(pb/expand-source-file-list dir config))
(pb/find-parse-time (pb/find-parse-time
"\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)")))) "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)"))))
(defun pb/hg-repo (dir) (defun pb/hg-repo (dir)
"Get the current hg repo for DIR." "Get the current hg repo for DIR."
(with-temp-buffer (pb/run-process-match "default = \\(.*\\)" dir "hg" "paths"))
(pb/run-process dir "hg" "paths")
(goto-char (point-min))
(re-search-forward "default = \\(.*\\)")
(match-string-no-properties 1)))
(defun pb/checkout-hg (name config dir) (defun pb/checkout-hg (name config dir)
"Check package NAME with config CONFIG out of hg into DIR." "Check package NAME with config CONFIG out of hg into DIR."
@ -274,15 +310,16 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
(cond (cond
((and (file-exists-p (expand-file-name ".hg" dir)) ((and (file-exists-p (expand-file-name ".hg" dir))
(string-equal (pb/hg-repo dir) repo)) (string-equal (pb/hg-repo dir) repo))
(print "checkout directory exists, updating...") (pb/princ-exists dir)
(pb/run-process dir "hg" "pull") (pb/run-process dir "hg" "pull")
(pb/run-process dir "hg" "update")) (pb/run-process dir "hg" "update"))
(t (t
(when (file-exists-p dir) (when (file-exists-p dir)
(delete-directory dir t nil)) (delete-directory dir t nil))
(print "cloning repository") (pb/princ-checkout repo dir)
(pb/run-process nil "hg" "clone" repo dir))) (pb/run-process nil "hg" "clone" repo dir)))
(pb/run-process dir "hg" "tip" "--style" "compact") (apply 'pb/run-process dir "hg" "log" "--style" "compact" "-l1"
(pb/expand-source-file-list dir config))
(pb/find-parse-time (pb/find-parse-time
"\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}\\)")))) "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}\\)"))))
@ -305,21 +342,13 @@ seconds; the server cuts off after 10 requests in 20 seconds.")
pkg-file)) pkg-file))
(defun pb/read-from-file (file-name) (defun pb/read-from-file (file-name)
"Read and return the Lisp data stored in FILE-NAME, or nil if no such file exists." "Read and return the Lisp data stored in FILE-NAME,or nil if no such file exists."
(when (file-exists-p file-name) (when (file-exists-p file-name)
(with-temp-buffer (with-temp-buffer
(insert-file-contents-literally file-name) (insert-file-contents-literally file-name)
(goto-char (point-min)) (goto-char (point-min))
(read (current-buffer))))) (read (current-buffer)))))
(defun pb/get-config (pkg-name)
"Get the configuration information for the given PKG-NAME."
(pb/read-from-file (format "epkgs/%s/.config" pkg-name)))
(defun pb/get-master (pkg-name)
"Get the configuration information for the given PKG-NAME."
(pb/read-from-file (format "epkgs/%s/master" pkg-name)))
(defun pb/create-tar (file dir &optional files) (defun pb/create-tar (file dir &optional files)
"Create a tar FILE containing the contents of DIR, or just FILES if non-nil. "Create a tar FILE containing the contents of DIR, or just FILES if non-nil.
@ -342,36 +371,39 @@ The file is written to `package-build-working-dir'."
"Get a vector of package info from the docstrings in FILE-PATH." "Get a vector of package info from the docstrings in FILE-PATH."
(when (file-exists-p file-path) (when (file-exists-p file-path)
(ignore-errors (ignore-errors
(save-window-excursion (with-temp-buffer
(find-file file-path) (insert-file-contents file-path)
;; next two lines are a hack for some packages that aren't ;; next few lines are a hack for some packages that aren't
;; commented properly. ;; commented properly.
(goto-char (point-min))
(forward-line)
(insert ";;; Version: 0")
(newline)
(goto-char (point-max)) (goto-char (point-max))
(insert (concat "\n;;; " (newline)
(file-name-nondirectory file-path) " ends here")) (insert ";;; " (file-name-nondirectory file-path) " ends here")
(flet ((package-strip-rcs-id (str) "0")) (flet ((package-strip-rcs-id (str) "0"))
(package-buffer-info)))))) (package-buffer-info))))))
(defun pb/get-pkg-file-info (file-path) (defun pb/get-pkg-file-info (file-path)
"Get a vector of package info from \"-pkg.el\" file FILE-PATH." "Get a vector of package info from \"-pkg.el\" file FILE-PATH."
(when (file-exists-p file-path) (when (file-exists-p file-path)
(let ((pkgfile-info (cdr (pb/read-from-file file-path)))) (let ((package-def (pb/read-from-file file-path)))
(vector (if (eq 'define-package (car package-def))
(nth 0 pkgfile-info) (let ((pkgfile-info (cdr package-def)))
(mapcar (vector
(lambda (elt) (nth 0 pkgfile-info)
(list (car elt) (version-to-list (cadr elt)))) (mapcar
(eval (nth 3 pkgfile-info))) (lambda (elt)
(nth 2 pkgfile-info) (list (car elt) (version-to-list (cadr elt))))
(nth 1 pkgfile-info))))) (eval (nth 3 pkgfile-info)))
(nth 2 pkgfile-info)
(nth 1 pkgfile-info)))
(error "No define-package found in %s" file-path)))))
(defun pb/expand-file-list (dir files)
"In DIR, expand FILES, some of which may be shell-style wildcards."
(let ((default-directory dir))
(mapcan 'file-expand-wildcards files)))
(defun pb/merge-package-info (pkg-info name version config) (defun pb/merge-package-info (pkg-info name version config)
"Return a version of PKG-INFO updated with NAME and VERSION. "Return a version of PKG-INFO updated with NAME, VERSION and info from CONFIG.
If PKG-INFO is nil, an empty one is created." If PKG-INFO is nil, an empty one is created."
(let* ((merged (or (copy-seq pkg-info) (let* ((merged (or (copy-seq pkg-info)
(vector name nil "No description available." version)))) (vector name nil "No description available." version))))
@ -405,50 +437,126 @@ If PKG-INFO is nil, an empty one is created."
desc desc
type))))) type)))))
(defun pb/archive-file-name (archive-entry)
"Return the path of the file in which the package for ARCHIVE-ENTRY is stored."
(expand-file-name (format "%s-%s.%s"
(car archive-entry)
(car (aref (cdr archive-entry) 0))
(if (eq 'single (aref (cdr archive-entry) 3))
"el"
"tar"))
package-build-archive-dir))
(defun pb/remove-archive (archive-entry)
"Remove ARCHIVE-ENTRY from archive-contents, and delete associated file.
Note that the working directory (if present) is not deleted by
this function, since the archive list may contain another version
of the same-named package which is to be kept."
(print (format "Removing archive: %s" archive-entry))
(let ((archive-file (pb/archive-file-name archive-entry)))
(when (file-exists-p archive-file)
(delete-file archive-file)))
(setq package-build-archive-alist
(remove archive-entry package-build-archive-alist))
(pb/dump-archive-contents))
(defun pb/read-recipes () (defun pb/read-recipes ()
"Return a list of data structures for all recipes in `package-build-recipes-dir'." "Return a list of data structures for all recipes in `package-build-recipes-dir'."
(mapcar 'pb/read-from-file (mapcar 'pb/read-from-file
(directory-files package-build-recipes-dir t "^[^.]"))) (directory-files package-build-recipes-dir t "^[^.]")))
(defun pb/expand-file-specs (dir specs &optional subdir)
"In DIR, expand SPECS, optionally under SUBDIR.
The result is a list of (SOURCE . DEST), where SOURCE is a source
file path and DEST is the relative path to which it should be copied."
(let ((default-directory dir)
(prefix (if subdir
(format "%s/" subdir)
"")))
(mapcan
(lambda (entry)
(if (consp entry)
(pb/expand-file-specs dir
(cdr entry)
(concat prefix (car entry)))
(mapcar (lambda (f)
(cons f (concat prefix (file-name-nondirectory f))))
(or (file-expand-wildcards entry)
(error "No matching file(s) found in %s: %s"
dir
entry)))))
specs)))
(defun pb/expand-config-file-list (dir config)
"In DIR, expand the :files for CONFIG using 'pb/expand-file-specs."
(pb/expand-file-specs dir (or (plist-get config :files) (list "*.el"))))
(defun pb/expand-source-file-list (dir config)
"Shorthand way to expand paths in DIR for source files listed in CONFIG."
(mapcar 'car (pb/expand-config-file-list dir config)))
(defun pb/copy-package-files (files source-dir target-dir)
"Copy FILES from SOURCE-DIR to TARGET-DIR.
FILES is a list of (SOURCE . DEST) relative filepath pairs."
(loop for (source-file . dest-file) in files
do (pb/copy-file
(expand-file-name source-file source-dir)
(expand-file-name dest-file target-dir))))
(defun pb/copy-file (file newname)
"Copy FILE to NEWNAME and create parent directories for NEWNAME if they don't exist."
(let ((newdir (file-name-directory newname)))
(unless (file-exists-p newdir)
(make-directory newdir t)))
(cond
((file-regular-p file)
(message "%s -> %s" file newname)
(copy-file file newname))
((file-directory-p file)
(message "%s => %s" file newname)
(copy-directory file newname))))
(defun pb/package-name-completing-read ()
"Prompt for a package name, returning a symbol."
(intern (completing-read "Package: " package-build-alist)))
;;; Public interface ;;; Public interface
(defun package-build-archive (name)
(defun package-build-archive (file-name) "Build a package archive for package NAME."
"Build a package archive for package FILE-NAME." (interactive (list (pb/package-name-completing-read)))
(interactive (list (completing-read "Package: " (let* ((file-name (symbol-name name))
(mapc 'car package-build-alist))))
(let* ((name (intern file-name))
(cfg (or (cdr (assoc name package-build-alist)) (cfg (or (cdr (assoc name package-build-alist))
(error "Cannot find package %s" file-name))) (error "Cannot find package %s" file-name)))
(pkg-cwd (pkg-cwd
(file-name-as-directory (file-name-as-directory
(expand-file-name file-name package-build-working-dir)))) (expand-file-name file-name package-build-working-dir))))
(message (format "\n;;; %s\n" file-name))
(let* ((version (pb/checkout name cfg pkg-cwd)) (let* ((version (pb/checkout name cfg pkg-cwd))
(files (pb/expand-file-list pkg-cwd (files (pb/expand-config-file-list pkg-cwd cfg))
(or (plist-get cfg :files)
(list "*.el"))))
(default-directory package-build-working-dir)) (default-directory package-build-working-dir))
(cond (cond
((not version) ((not version)
(print (format "Unable to check out repository for %s" name))) (message "Unable to check out repository for %s" name))
((= 1 (length files)) ((= 1 (length files))
(let* ((pkgsrc (expand-file-name (car files) pkg-cwd)) (let* ((pkg-source (expand-file-name (caar files) pkg-cwd))
(pkgdst (expand-file-name (pkg-target (expand-file-name
(concat file-name "-" version ".el") (concat file-name "-" version ".el")
package-build-archive-dir)) package-build-archive-dir))
(pkg-info (pb/merge-package-info (pkg-info (pb/merge-package-info
(pb/get-package-info pkgsrc) (pb/get-package-info pkg-source)
file-name file-name
version version
cfg))) cfg)))
(print pkg-info) (when (file-exists-p pkg-target)
(when (file-exists-p pkgdst) (delete-file pkg-target t))
(delete-file pkgdst t)) (copy-file pkg-source pkg-target)
(copy-file pkgsrc pkgdst)
(pb/add-to-archive-contents pkg-info 'single))) (pb/add-to-archive-contents pkg-info 'single)))
(t ((< 1 (length files))
(let* ((pkg-dir (concat file-name "-" version)) (let* ((pkg-dir (concat file-name "-" version))
;; TODO: What if the upstream "-pkg.el" file is in a subdir?
(pkg-file (concat file-name "-pkg.el")) (pkg-file (concat file-name "-pkg.el"))
(pkg-info (pkg-info
(pb/merge-package-info (pb/merge-package-info
@ -461,54 +569,75 @@ If PKG-INFO is nil, an empty one is created."
version version
cfg))) cfg)))
(print pkg-info) (when (file-exists-p pkg-dir)
(copy-directory file-name pkg-dir) (delete-directory pkg-dir t nil))
(pb/write-pkg-file (expand-file-name (pb/copy-package-files files pkg-cwd pkg-dir)
pkg-file
(file-name-as-directory (pb/write-pkg-file (expand-file-name pkg-file
(expand-file-name (file-name-as-directory
pkg-dir (expand-file-name
package-build-working-dir))) pkg-dir
package-build-working-dir)))
pkg-info) pkg-info)
(when files
(add-to-list 'files pkg-file))
(pb/create-tar (pb/create-tar
(expand-file-name (expand-file-name
(concat file-name "-" version ".tar") package-build-archive-dir) (concat file-name "-" version ".tar") package-build-archive-dir)
pkg-dir pkg-dir
files) (append (mapcar 'cdr files) (list pkg-file)))
(delete-directory pkg-dir t nil) (delete-directory pkg-dir t nil)
(pb/add-to-archive-contents pkg-info 'tar)))) (pb/add-to-archive-contents pkg-info 'tar)))
(pb/dump-archive-contents))))
(defun package-build-archives (&rest pkgs) (t (error "Unable to find files matching recipe patterns")))
"Build archives for packages PKGS." (pb/dump-archive-contents)
(interactive) file-name)))
(mapc 'package-build-archive pkgs))
(defun package-build-archives-ignore-errors (&rest pkgs) (defun package-build-archive-ignore-errors (pkg)
"Build archives for packages PKGS. Ignore errors." "Build archive for package PKG, ignoring any errors."
(interactive) (interactive (list (pb/package-name-completing-read)))
(mapc (lambda (pkg) (ignore-errors (package-build-archive pkg))) pkgs)) (let* ((debug-on-error t)
(debug-on-signal t)
(pb/debugger-return nil)
(debugger (lambda (&rest args)
(setq pb/debugger-return (with-output-to-string
(backtrace))))))
(condition-case err
(package-build-archive pkg)
('error
(message "%s" (error-message-string err))
nil))))
(defun package-build-all () (defun package-build-all ()
"Build all packages in the `package-build-alist'." "Build all packages in the `package-build-alist'."
(interactive) (interactive)
(apply 'package-build-archives-ignore-errors (let ((failed (loop for pkg in (mapcar 'car package-build-alist)
(mapcar 'symbol-name (mapcar 'car package-build-alist)))) when (not (package-build-archive-ignore-errors pkg))
collect pkg)))
(if (not failed)
(princ "\nSuccessfully Compiled All Packages\n")
(princ "\nFailed to Build the Following Packages\n")
(princ (mapconcat 'symbol-name failed "\n"))))
(package-build-cleanup))
(defun package-build-cleanup ()
"Remove previously-built packages that no longer have recipes."
(interactive)
(let* ((known-package-names (mapcar 'car package-build-alist))
(stale-archives (loop for built in package-build-archive-alist
when (not (memq (car built) known-package-names))
collect built)))
(mapc 'pb/remove-archive stale-archives)))
(defun package-build-initialize () (defun package-build-initialize ()
"Load the recipe and archive-contents files." "Load the recipe and archive-contents files."
(interactive) (interactive)
(setq (setq package-build-alist (pb/read-recipes)
package-build-alist (pb/read-recipes) package-build-archive-alist
package-build-archive-alist (cdr (pb/read-from-file (cdr (pb/read-from-file
(expand-file-name "archive-contents" (expand-file-name "archive-contents"
package-build-archive-dir))))) package-build-archive-dir)))))
(package-build-initialize) (package-build-initialize)

4
recipes/.dir-locals.el Normal file
View file

@ -0,0 +1,4 @@
((nil . ((eval . (when (and (buffer-file-name)
(file-regular-p (buffer-file-name))
(string-match-p "^[^.]" (buffer-file-name)))
(emacs-lisp-mode))))))

View file

@ -1,3 +1,3 @@
(auto-complete :repo "m2ym/auto-complete" :fetcher github :files (auto-complete :repo "m2ym/auto-complete" :fetcher github :files
("*.el" "dict/*")) ("*.el" "dict"))

4
recipes/autopair Normal file
View file

@ -0,0 +1,4 @@
(autopair
:fetcher svn
:url "http://autopair.googlecode.com/svn/trunk/"
:files ("autopair.el"))

View file

@ -0,0 +1,3 @@
(color-theme-buffer-local :fetcher github
:repo "vic/color-theme-buffer-local"
:files ("color-theme-buffer-local.el"))

2
recipes/disk Normal file
View file

@ -0,0 +1,2 @@
(disk :fetcher wiki)

1
recipes/egg Normal file
View file

@ -0,0 +1 @@
(egg :repo "byplayer/egg" :fetcher github)

View file

@ -1,3 +1,3 @@
(elscreen (elscreen
:repo shosti/elscreen :repo "shosti/elscreen"
:fetcher github) :fetcher github)

3
recipes/emacs-setup Normal file
View file

@ -0,0 +1,3 @@
(emacs-setup
:fetcher github
:repo "echosa/emacs-setup")

3
recipes/erlang Normal file
View file

@ -0,0 +1,3 @@
(erlang :repo "erlang/otp"
:fetcher github
:files ("lib/tools/emacs/*.el"))

1
recipes/escreen Normal file
View file

@ -0,0 +1 @@
(escreen :repo "emacsmirror/escreen" :fetcher github)

View file

@ -1,3 +1,5 @@
(ess :repo "milkypostman/ESS" :fetcher github :files (ess :repo "milkypostman/ESS" :fetcher github :files
("*.el" "lisp/*.el" "etc/*")) ("*.el" ("lisp" "lisp/*.el") ("etc" "etc/*")))

View file

@ -1,2 +0,0 @@
(evil-surround :repo "timcharper/evil-surround" :fetcher github)

View file

@ -1,3 +1,4 @@
(flymake-perlcritic :repo "illusori/emacs-flymake-perlcritic" :fetcher github :files (flymake-perlcritic :repo "illusori/emacs-flymake-perlcritic"
("*.el" "bin/flymake_perlcritic")) :fetcher github
:files ("*.el" ("bin" "bin/flymake_perlcritic")))

View file

@ -1,3 +1,4 @@
(git-blame :repo "tsgates/git-emacs" :fetcher github :files (git-blame :repo "tsgates/git-emacs"
("git-blame.el")) :fetcher github
:files ("git-blame.el"))

4
recipes/gnuplot Normal file
View file

@ -0,0 +1,4 @@
(gnuplot
:repo "bruceravel/gnuplot-mode"
:fetcher github
:files ("gnuplot.el" "gnuplot-gui.el"))

1
recipes/helm-git Normal file
View file

@ -0,0 +1 @@
(helm-git :repo "maio/helm-git" :fetcher github)

View file

@ -1,3 +1,4 @@
(helm-projectile :repo "bbatsov/projectile" :fetcher github :files (helm-projectile :repo "bbatsov/projectile"
("helm-projectile.el")) :fetcher github
:files ("helm-projectile.el"))

View file

@ -1,3 +1,16 @@
(icicles :fetcher wiki :files (icicles
("icicles.el" "icicles-chg.el" "icicles-cmd1.el" "icicles-cmd2.el" "icicles-doc1.el" "icicles-doc2.el" "icicles-face.el" "icicles-fn.el" "icicles-mac.el" "icicles-mcmd.el" "icicles-mode.el" "icicles-opt.el" "icicles-var.el")) :fetcher wiki
:files ("icicles.el"
"icicles-chg.el"
"icicles-cmd1.el"
"icicles-cmd2.el"
"icicles-doc1.el"
"icicles-doc2.el"
"icicles-face.el"
"icicles-fn.el"
"icicles-mac.el"
"icicles-mcmd.el"
"icicles-mode.el"
"icicles-opt.el"
"icicles-var.el"))

View file

@ -1,3 +1,4 @@
(inflections :repo "milkypostman/jump.el" :fetcher github :files (inflections :repo "milkypostman/jump.el"
("inflections.el")) :fetcher github
:files ("inflections.el"))

3
recipes/inkpot-theme Normal file
View file

@ -0,0 +1,3 @@
(inkpot-theme
:repo "siovan/emacs24-inkpot"
:fetcher github)

3
recipes/jabber-mode Normal file
View file

@ -0,0 +1,3 @@
(jabber-mode
:url "git://emacs-jabber.git.sourceforge.net/gitroot/emacs-jabber/emacs-jabber"
:fetcher git)

1
recipes/jade-mode Normal file
View file

@ -0,0 +1 @@
(jade-mode :fetcher github :repo "brianc/jade-mode" :files ("jade-mode.el"))

View file

@ -0,0 +1,3 @@
(latex-pretty-symbols
:url "https://bitbucket.org/mortiferus/latex-pretty-symbols.el"
:fetcher hg)

View file

@ -0,0 +1,3 @@
(load-theme-buffer-local :fetcher github
:repo "vic/color-theme-buffer-local"
:files ("load-theme-buffer-local.el"))

View file

@ -1,3 +1,3 @@
(lua-mode (lua-mode
:repo "immerrr/lua-mode" :repo "immerrr/lua-mode"
:fetcher github) :fetcher github)

View file

@ -1,3 +1,5 @@
(markdown-mode+ :repo "milkypostman/markdown-mode-plus" :fetcher github :files (markdown-mode+ :repo "milkypostman/markdown-mode-plus"
("markdown-mode+.el" "HTML as Markdown in Emacs.applescript")) :fetcher github
:files ("markdown-mode+.el"
"HTML as Markdown in Emacs.applescript"))

View file

@ -1,3 +1,4 @@
(melpa :repo "milkypostman/melpa" :fetcher github :files (melpa :repo "milkypostman/melpa"
("melpa.el")) :fetcher github
:files ("melpa.el"))

1
recipes/minimap Normal file
View file

@ -0,0 +1 @@
(minimap :url "git://randomsample.de/minimap.git" :fetcher git)

3
recipes/mysql2sqlite Normal file
View file

@ -0,0 +1,3 @@
(mysql2sqlite
:fetcher github
:repo "echosa/emacs-mysql2sqlite")

View file

@ -1,3 +1,4 @@
(org-toodledo :repo "christopherjwhite/org-toodledo" :fetcher github :files (org-toodledo :repo "christopherjwhite/org-toodledo"
("http-post-simple.el" "org-toodledo.el" "w3mexcerpt.el")) :fetcher github
:files ("http-post-simple.el" "org-toodledo.el" "w3mexcerpt.el"))

1
recipes/outline-magic Normal file
View file

@ -0,0 +1 @@
(outline-magic :fetcher wiki)

View file

@ -1,3 +1,4 @@
(paredit :url "http://mumble.net/~campbell/emacs/paredit" :fetcher darcs :files (paredit :url "http://mumble.net/~campbell/emacs/paredit"
("paredit.el")) :fetcher darcs
:files ("paredit.el"))

View file

@ -1,3 +1,4 @@
(pkgbuild-mode :repo "juergenhoetzel/pkgbuild-mode" :fetcher github :files (pkgbuild-mode :repo "juergenhoetzel/pkgbuild-mode"
("pkgbuild-mode.el")) :fetcher github
:files ("pkgbuild-mode.el"))

4
recipes/pony-mode Normal file
View file

@ -0,0 +1,4 @@
(pony-mode
:repo "davidmiller/pony-mode"
:fetcher github
:files ("src/*.el" "snippets"))

View file

@ -1,3 +1,4 @@
(popup :repo "m2ym/popup-el" :fetcher github :files (popup :repo "m2ym/popup-el"
("popup.el")) :fetcher github
:files ("popup.el"))

1
recipes/prelude-c Normal file
View file

@ -0,0 +1 @@
(prelude-c :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-c.el"))

1
recipes/prelude-clojure Normal file
View file

@ -0,0 +1 @@
(prelude-clojure :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-clojure.el"))

1
recipes/prelude-coffee Normal file
View file

@ -0,0 +1 @@
(prelude-coffee :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-coffee.el"))

View file

@ -0,0 +1 @@
(prelude-common-lisp :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-common-lisp.el"))

1
recipes/prelude-css Normal file
View file

@ -0,0 +1 @@
(prelude-css :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-css.el"))

View file

@ -0,0 +1 @@
(prelude-emacs-lisp :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-emacs-lisp.el"))

1
recipes/prelude-haskell Normal file
View file

@ -0,0 +1 @@
(prelude-haskell :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-haskell.el"))

1
recipes/prelude-js Normal file
View file

@ -0,0 +1 @@
(prelude-js :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-js.el"))

1
recipes/prelude-latex Normal file
View file

@ -0,0 +1 @@
(prelude-latex :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-latex.el"))

1
recipes/prelude-lisp Normal file
View file

@ -0,0 +1 @@
(prelude-lisp :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-lisp.el"))

View file

@ -0,0 +1 @@
(prelude-mediawiki :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-mediawiki.el"))

1
recipes/prelude-perl Normal file
View file

@ -0,0 +1 @@
(prelude-perl :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-perl.el"))

View file

@ -0,0 +1 @@
(prelude-programming :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-programming.el"))

1
recipes/prelude-ruby Normal file
View file

@ -0,0 +1 @@
(prelude-ruby :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-ruby.el"))

1
recipes/prelude-scheme Normal file
View file

@ -0,0 +1 @@
(prelude-scheme :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-scheme.el"))

1
recipes/prelude-scss Normal file
View file

@ -0,0 +1 @@
(prelude-scss :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-scss.el"))

1
recipes/prelude-xml Normal file
View file

@ -0,0 +1 @@
(prelude-xml :repo "bbatsov/prelude-modules" :fetcher github :files ("prelude-xml.el"))

View file

@ -1,2 +1 @@
(pretty-mode :repo "emacsmirror/pretty-mode" :fetcher github) (pretty-mode :fetcher wiki)

View file

@ -1,3 +1,4 @@
(projectile :repo "bbatsov/projectile" :fetcher github :files (projectile :repo "bbatsov/projectile"
("projectile.el")) :fetcher github
:files ("projectile.el"))

4
recipes/pyregexp Normal file
View file

@ -0,0 +1,4 @@
(pyregexp :repo "benma/pyregexp"
:fetcher github
:files ("pyregexp.el" "pyregexp.py"))

1
recipes/python-magic Normal file
View file

@ -0,0 +1 @@
(python-magic :fetcher wiki)

View file

@ -1,2 +1,2 @@
(rainbow-delimiters :fetcher wiki) (rainbow-delimiters :fetcher github :repo "jlr/rainbow-delimiters")

View file

@ -1,3 +1,4 @@
(ruby-compilation :repo "eschulte/rinari" :fetcher github :files (ruby-compilation :repo "eschulte/rinari"
("util/ruby-compilation.el")) :fetcher github
:files ("util/ruby-compilation.el"))

View file

@ -1,3 +1,3 @@
(ruby-interpolation (ruby-interpolation
:repo "leoc/ruby-interpolation.el" :repo "leoc/ruby-interpolation.el"
:fetcher github) :fetcher github)

View file

@ -1,3 +1,4 @@
(ruby-mode :url "http://svn.ruby-lang.org/repos/ruby/trunk/misc" :fetcher svn :files (ruby-mode :url "http://svn.ruby-lang.org/repos/ruby/trunk/misc"
("ruby-mode.el")) :fetcher svn
:files ("ruby-mode.el"))

1
recipes/ruby-tools Normal file
View file

@ -0,0 +1 @@
(ruby-tools :repo "rejeep/ruby-tools" :fetcher github)

View file

@ -1,3 +1,4 @@
(slime :repo "nablaone/slime" :fetcher github :files (slime :repo "nablaone/slime"
("slime.el")) :fetcher github
:files ("slime.el"))

View file

@ -1,3 +1,4 @@
(slime-ritz :repo "pallet/ritz" :fetcher github :files (slime-ritz :repo "pallet/ritz"
("src/main/elisp/slime-ritz.el")) :fetcher github
:files ("src/main/elisp/slime-ritz.el"))

1
recipes/smooth-scrolling Normal file
View file

@ -0,0 +1 @@
(smooth-scrolling :repo "jbondeson/smooth-scrolling" :fetcher github)

View file

@ -1,3 +1,4 @@
(starter-kit-bindings :repo "technomancy/emacs-starter-kit" :fetcher github :files (starter-kit-bindings :repo "technomancy/emacs-starter-kit"
("modules/starter-kit-bindings.el")) :fetcher github
:files ("modules/starter-kit-bindings.el"))

View file

@ -1,3 +1,4 @@
(starter-kit-eshell :repo "technomancy/emacs-starter-kit" :fetcher github :files (starter-kit-eshell :repo "technomancy/emacs-starter-kit"
("modules/starter-kit-eshell.el")) :fetcher github
:files ("modules/starter-kit-eshell.el"))

View file

@ -1,3 +1,4 @@
(starter-kit-js :repo "technomancy/emacs-starter-kit" :fetcher github :files (starter-kit-js :repo "technomancy/emacs-starter-kit"
("modules/starter-kit-js.el")) :fetcher github
:files ("modules/starter-kit-js.el"))

View file

@ -1,3 +1,4 @@
(starter-kit-lisp :repo "technomancy/emacs-starter-kit" :fetcher github :files (starter-kit-lisp :repo "technomancy/emacs-starter-kit"
("modules/starter-kit-lisp.el")) :fetcher github
:files ("modules/starter-kit-lisp.el"))

View file

@ -1,3 +1,4 @@
(starter-kit-perl :repo "technomancy/emacs-starter-kit" :fetcher github :files (starter-kit-perl :repo "technomancy/emacs-starter-kit"
("modules/starter-kit-perl.el")) :fetcher github
:files ("modules/starter-kit-perl.el"))

View file

@ -1,3 +1,4 @@
(starter-kit-ruby :repo "technomancy/emacs-starter-kit" :fetcher github :files (starter-kit-ruby :repo "technomancy/emacs-starter-kit"
("modules/starter-kit-ruby.el")) :fetcher github
:files ("modules/starter-kit-ruby.el"))

1
recipes/stylus-mode Normal file
View file

@ -0,0 +1 @@
(stylus-mode :fetcher github :repo "brianc/jade-mode" :files ("stylus-mode.el"))

2
recipes/surround Normal file
View file

@ -0,0 +1,2 @@
(surround :repo "timcharper/evil-surround" :fetcher github)

1
recipes/sws-mode Normal file
View file

@ -0,0 +1 @@
(sws-mode :fetcher github :repo "brianc/jade-mode" :files ("sws-mode.el"))

1
recipes/tango-2-theme Normal file
View file

@ -0,0 +1 @@
(tango-2-theme :repo "2024464" :fetcher github)

1
recipes/tidy Normal file
View file

@ -0,0 +1 @@
(tidy :fetcher wiki)

View file

@ -1,2 +1,2 @@
(vimgolf :repo "timvisher/vimgolf" :fetcher github) (vimgolf :repo "timvisher/vimgolf" :fetcher github :files ("emacs/vimgolf.el"))

View file

@ -1 +1 @@
(melpa :repo "bnbeckwith/writegood-mode" :fetcher github) (writegood-mode :repo "bnbeckwith/writegood-mode" :fetcher github)

View file

@ -1,3 +1,4 @@
(yari :repo "hron/yari.el" :fetcher github :files (yari :repo "hron/yari.el"
("yari.el")) :fetcher github
:files ("yari.el"))

View file

@ -1,3 +1,4 @@
(yasnippet :repo "capitaomorte/yasnippet" :fetcher github :files (yasnippet :repo "capitaomorte/yasnippet"
("*.el" "snippets")) :fetcher github
:files ("*.el" "snippets"))

View file

@ -1,3 +1,4 @@
(zenburn-theme :repo "bbatsov/zenburn-emacs" :fetcher github :files (zenburn-theme :repo "bbatsov/zenburn-emacs"
("zenburn-theme.el")) :fetcher github
:files ("zenburn-theme.el"))

4
recipes/zf-mode Normal file
View file

@ -0,0 +1,4 @@
(zf-mode
:fetcher github
:repo "echosa/zf-mode"
:files ("*.el" "ZFMode" "bundled"))