NAME
Emacs::Run - use emacs from perl via the shell
SYNOPSIS
use Emacs::Run;
my $er = Emacs::Run->new();
my $major_version = $er->emacs_major_version;
if ($major_version > 22) {
print "You have a recent version of emacs\n";
}
# use extra emacs lisp libraries, then get emacs settings
my $er = Emacs::Run->new({
emacs_libs => [ '~/lib/my-elisp.el',
'/usr/lib/site-emacs/stuff.el' ],
});
my $emacs_load_path_aref = $er->get_load_path;
my $email = $er->get_variable( 'user-mail-address' );
my $name = $er->eval_function( 'user-full-name' );
# suppress the use of the usual emacs init (e.g. ~/.emacs)
my $er = Emacs::Run->new({
load_emacs_init => 0,
});
my $result = $er->eval_elisp( '(print (+ 2 2))' ); # that's "4"
# the eval_elisp_full_emacs method works with a full externally spawned emacs
# (for code that won't run under '--batch')
my $elisp_initialize =
qq{
(defvar my-temp-var "$text")
(insert "This insert will not appear in output.")
};
my $elisp =
qq{
(insert my-temp-var)
(downcase-region (point-min) (point-max))
(my-test-lib-do-something)
};
my @emacs_libs = ( $dot_emacs, 'my-test-lib' );
my $er = Emacs::Run->new({
load_no_inits => 1,
emacs_libs => \@emacs_libs,
});
my $modified_lines_aref =
$er->eval_elisp_full_emacs( {
elisp_initialize => $elisp_initialize,
output_file => $name_list_file, # omit to use temp file
elisp => $elisp,
DESCRIPTION
Emacs::Run is a module that provides utilities to work with emacs when run from perl as an external process.
The emacs "editor" has some command-line options ("--batch" and so on) that turn emacs into a lisp interpreter for the elisp dialect.
This module provides methods to allow perl code to portably use these features of emacs for tasks such as:
Probe the system's emacs installation to get the installed version, the user's current load-path, and so on.
Run chunks of emacs lisp code without worrying too much about the details of quoting issues and loading libraries and so on.
METHODS
- new
-
Creates a new Emacs::Run object.
Takes a hashref as an argument, with named fields identical to the names of the object attributes. These attributes are:
- emacs_path
-
By default, this code looks for an external program called 'emacs' letting the system (e.g. the shell's PATH environment variable) find the program if it can. If you have multiple emacsen installed in different places and/or under different names, you can choose which one will be used by setting this attribute.
- ipc_capture_handle
-
The IPC::Capture handle, used by default (if available) to run shell commands.
- redirector
-
A code that specifies how (by default) to handle the STDOUT and STDERR streams from elisp run by methods such as "eval_elisp" and "run_elisp_on_file", when IPC::Capture is in use.
As of this writing (version 0.10), this may be one of three values (for details on each of these, see IPC::Capture):
- stdout_only
- stderr_only
- all_output
Note that the somewhat unusual all_separated redirector setting is not supported by this module.
- use_shell_directly
-
Flag to indicate you always want to shell out directly rather than use IPC::Capture.
- shell_output_director
-
The analog of the "redirector" for when using the shell directly: a Bourne shell redirection code (ala '2>&1').
Setting this also causes "use_shell_directly" to be set.
- before_hook
-
A string inserted into the built-up emacs commands immediately after "--batch", but before any thing else is executed. This is a good place to insert additional invocation options such as "--multibyte" or "--unibyte". See </append_to_before_hook>.
- load_emacs_init
-
Defaults to 1, if set to a false value, will suppress the use of the user's emacs init file (e.g. "~/.emacs").
- load_site_init
-
Defaults to 1, if set to a false value, will suppress the use of the system "site-start.el" file (which loads before the user's init file).
- load_default_init
-
Defaults to 1, if set to a false value, will suppress the use of the system "default.el" file (which loads after the user's init file).
- load_no_inits
-
A convenience flag, which may be set to disable all three types of emacs init files in one step. Overrides the other three.
- emacs_libs
-
A list of emacs libraries (with or without paths) to be loaded automatically. This is provided as a convenience for quick use. To take full control over how your emacs libraries are handled, see "lib_data".
- default_priority
-
The global default for how all the emacs libraries should be loaded. Normally this is set to "requested", but it can be set to "needed".
A 'requested' library will be silently skipped if it is not available (and any elisp code using it may need to do something like 'featurep' checks to adapt to it's absence). A 'needed' file will cause an error to occur if it is not available. Note: this error does not occur during object instantiation, but only after a method is called that needs to load the libraries (e.g. "eval_function" "get_variable", "eval_elisp", "run_elisp_on_file", etc).
- lib_data
-
Note: using "emacs_libs" is usually preferrable to "lib_data".
lib_data is the internal representation that </emacs_libs> is converted into, but the client programmer is provided access to it to cover any unusual needs.
The structure of lib_data is an array of arrays of two elements each, the first element is the library name (a string, with or without path), the second element is a hash of library attributes: 'priority' (which can be 'requested' or 'needed') and 'type' (which can be 'file' or 'lib').
Example:
$lib_data = [ [ 'dired', { type=>'lib', priority=>'needed' } ], [ '/tmp/my-load-path.el', { type=>'file', priority=>'requested' } ], [ '/tmp/my-elisp.el', { type=>'file', priority=>'needed' } ], ];
emacs library attributes:
- priority
-
A 'requested' library will be silently skipped if it is not available, but if a 'needed' file is not available it's regarded as an error condition. The default priority is 'requested', but that can be changed via the "default_priority" attribute. See "default_priority" for more details.
- type
-
A library of type 'file' should be a filesystem path to a file containing a library of emacs lisp code. A library of type 'lib' is specified by just the basename of the file (sans path or extension), and we will search for it looking in the places specified in the emacs variable load-path. When neither is specified, this module guesses the lib is a file if it looks that way (i.e it has a path and/or extension).
If both lib_data and emacs_libs are used, the lib_data libraries are loaded first, followed by the emacs_libs libraries.
These attributes are used to pass information to the client programmer, they should be regarded as read-only:
- emacs_version
-
The version number of emacs in use: this is set automatically by the "probe_emacs_version" method during object initialization.
- emacs_type
-
The flavor of emacs in use, e.g. 'Gnu Emacs'. Set automatically by the "probe_emacs_version" method during object initialization.
There are also a number of object attributes intended largely for internal use. The client programmer has access to these, but is not expected to need it. These are documented in "internal attributes".
- init
-
Method that initializes object attributes and then locks them down to prevent accidental creation of new ones.
Any class that inherits from this one should have an "init" of it's own that calls this "init".
Simple Emacs Invocations
Some simple methods for obtaining information from your emacs installation.
These methods default to returning STDOUT, suppressing anything sent to STDERR. This behavior can be overridden: see "Controlling Output Redirection".
- eval_function
-
Given the name of an emacs function, this runs the function and returns the value from emacs (when started with the the .emacs located in $HOME, if one is found). After the function name, an optional array reference may be supplied to pass through a list of simple arguments (limited to strings) to the elisp function. And further, an optional hash reference may follow that to specify options to the "eval_function" method.
By default the returned output is STDOUT only but this behavior can be overridden: See "Controlling Output Redirection".
As with "get_variable", this uses the emacs 'print' function internally.
Examples:
my $name = $er->eval_function( 'user-full-name' ); $er->eval_function( 'extract-doctrings-generate-html-for-elisp-file', [ "$input_elisp_file", "$output_file", "The extracted docstrings" ] );
- get_variable
-
Given the name of an emacs variable, returns the value from emacs (when started with the the .emacs located in $HOME, if one is found),
Internally, this uses the emacs 'print' function, which can handle variables containing complex data types, but the return value will be a "printed representation" that may make more sense to emacs than to perl code. For example, the "load-path" variable might look like:
("/home/grunt/lib" "/usr/lib/emacs/site-lisp" "/home/xtra/lib")
See "get_load_path" below for a more perl-friendly way of doing this.
- get_load_path
-
Returns the load-path from emacs (by default, using the user's .emacs, if it can be found) as a reference to a perl array.
Changing the $HOME environment variable before running this method results in loading the .emacs file located in the new $HOME.
- probe_for_option_no_splash
-
Looks for the emacs command line option "--no-splash", returning true (1) if it exists, and false (0) otherwise.
Running Elisp
These are general methods that run pieces of emacs lisp code.
The detailed behavior of these methods have a number of things in common:
By default the method first loads the user's initialization file ("$HOME/.emacs") if it can be found. It will also try to load the libraries listed in the "emacs_libs" and/or "lib_data" attributes.
There are object attribute settings that can be used to suppress loading any of the various init files. See "new" for the full list. In particular, if the "load_emacs_init" attribute has been turned off, it will not try to load the .emacs file.
Unless specified otherwise, the methods return the output from the elisp code with STDOUT and STDERR mixed together, though this behavior can be overridden. See "Controlling Output Redirection".
(The advantage of intermixing STDOUT and STDERR is that the emacs functions 'message' as well as 'print' both may be used for interesting output. The disadvantage is that you may have many inane messages from emacs sent to STDERR such as 'Loading library so-and-so')
- eval_elisp
-
Given a string containing a chunk of elisp code this method runs it by invoking emacs in batch mode.
Example:
my $result = $er->eval_elisp( '(print (+ 2 2))' );
- run_elisp_on_file
-
Given a file name, and some emacs lisp code (which presumably modifies the current buffer), this method opens the file, runs the code on it, and then saves the file.
Returns whatever value the elisp returns.
Example usage: $self->run_elisp_on_file( $filename, $elisp );
- eval_elisp_full_emacs
-
Runs the given chunk(s) of elisp using a temporarily launched full scale emacs (not just via --batch mode).
Of necessity, this emacs sub-process must communicate via a file (similar to "run_elisp_on_file"), though the output is returned from this routine in the form of an array reference of the lines of the file.
The main chunk of elisp run by this routine should be designed to output to the current buffer (e.g. via "insert" calls).
The output from elisp functions such as "message" and "print" is not captured.
As an option, a seperate chunk of initialization elisp may also be passed in: it will be run before the output file buffer is opened, and hence any modification it makes to the current buffer will be ignored.
If the "output_filename" is not supplied, a temporary file will be created and deleted afterwards. If the name is supplied, the output file will be still exist afterwards (note: any existing contents will be over-written).
The current buffer is saved at the end of the processing (so there's no need to include a "save-buffer" call in the elisp).
All arguments are passed into this method via a hashref of "options" (though there's one "option" that is required, if you want it to do anything: "elisp"). These are:
elisp_initialize output_file elisp
Some example usages:
my $er = Emacs::Run->new({ load_no_inits = 1, emacs_libs => [ '~/lib/my-elisp.el', '/usr/lib/site-emacs/stuff.el' ], });
Using just the required "option":
my $elisp = q{ (insert-file "$input_file") (downcase-region (point-min) (point-max)) }; my $output = $er->eval_elisp_full_emacs( { elisp => $elisp, } );
Using all options:
my $output = $er->eval_elisp_full_emacs( { elisp_initialize => $elisp_initialize, output_file => $output_file, elisp => $elisp, } );
This method only uses some of the usual Emacs::Run framework. For example, since it always does an "exec", many object settings have no meaning here: e.g. "use_shell_directly", "redirector", "shell_output_director".
If "load_no_inits" is set, the emacs init files will be ignored (via "-q") unless, of course, they're passed in manually in the "emacs_libs" array reference. The three individual init settings flags have no effect on this method ("load_emacs_init", "load_site_init", "load_default_init").
Adding libraries to emacs_libs will not automatically add their locations to the load-path (because the "ec_lib_loader" system is not in use here).
- full_emacs_done
-
Internally used routine.
When it looks as though the child process run by eval_elisp_full_emacs is finished, this returns true.
At present, this just watches to see when the output_file has been written.
INTERNAL METHODS
The following methods are intended primarily for internal use.
Note: the common "leading underscore" naming convention is not used here.
Utility Methods
- quote_elisp
-
Routine to quote elisp code before feeding it into an emacs batch shell command. Used internally by methods such as "eval_elisp".
This just adds a single backslash to all the double-quote characters (essentially an empirically determined algorithm, i.e. hack).
Example usage:
$elisp = $self->quote_elisp( $elisp ); $emacs_cmd = qq{ emacs --batch --eval "$elisp" 2>&1 }; my $return = qx{ $emacs_cmd };
- progn_wrapper
-
Takes a chunk of elisp, and adds a "progn" around it, to help make multi-line chunks of elisp Just Work.
- parse_ec_string
-
Takes a chunk of emacs command-line invocation in string form and converts it to a list form (suitable for feeding into "system" or "exec", stepping around the shell).
Returns an aref of tokens (filenames, options, option-arguments).
Limitation:
The '--' option (which indicates that all following tokens are file names, even if they begin with a hyphen) is not yet handled correctly here.
- clean_return_value
-
Cleans up a given string, trimming unwanted leading and trailing blanks and double quotes.
This is intended to be used with elisp that uses the 'print' function. Note that it is limited to elisp with a single print of a result: multiple prints will leave embedded quote-newline pairs in the output.
- redirector_to_sod
-
Convert a redirector code into the equivalent shell_output_director (Bourne shell).
Initialization Phase Methods
The following routines are largely used internally in the object initialization phase.
- process_emacs_libs_addition
-
Goes through the given list of emacs_libs, and converts the names into the lib_data style of data structure, appending it to lib_data.
Note that this method works on the given argument, without reference to the object's "emacs_libs" field.
Returns a reference to a structure containing the new additions to lib_data.
- process_emacs_libs_reset
-
This converts the list of emacs_libs into the lib_data style of structure much like "process_emacs_libs_addition", but this method resets the lib_data field to the given value at init time (if any) before appending the new data.
Defaults to using the object's "emacs_libs" setting.
Returns a reference to a structure containing the additions to lib_data from emacs_libs.
- set_up_ec_lib_loader
-
Initializes the ec_lib_loader attribute by scanning for the appropriate emacs init file(s) and processing the list(s) of emacs libraries specified in the object data.
Returns the newly defined $ec_lib_loader string.
This routine is called by "init" during object initialization.
- Generation of Emacs Command Strings to Load Libraries
-
These are routines run by "set_up_ec_lib_loader" that generate a string that can be included in an emacs command line invocation to load certain libraries. Note the naming convention: "generate emacs command-line" => "genec_".
- genec_load_emacs_init
-
Generates an emacs command line fragment to load the emacs_init file(s) as appropriate.
Side effect: zeroes out the ec_lib_loader before rebuilding with inits only.
- Genec Methods Called Dynamically
-
The following is a set of four routines used by "set_ec_lib_loader" to generate a string that can be included in an emacs command line invocation to load the given library. The methods here are named according to the pattern:
"genec_loader_$type_$priority"
All of these methods return the generated string, but also append it to the "ec_lib_loader" attribute.
- genec_loader_lib_needed
- genec_loader_file_needed
- genec_loader_lib_requested
- genec_loader_file_requested
Emacs probes
Methods that return information about the emacs installation.
- probe_emacs_version
-
Returns the version of the emacs program stored on your system. This is called during the object initialization phase.
It checks the emacs specified in the object's emacs_path (which defaults to the simple command "emacs", sans any path), and returns the version.
As a side-effect, it sets a number of object attributes with details about the emacs version:
emacs_version emacs_major_version emacs_type
By default the returned output includes just STDOUT and not STDERR (and the object attribute "shell_output_director" is ignored), but this behavior can be overridden by setting a field named shell_output_director in the options hashref.
An example of that usage:
my $version_text = $er->probe_emacs_version( { shell_output_director => '2>&1', } );
- parse_emacs_version_string
-
The emacs version string returned from running "emacs --version" is parsed by this routine, which picks out the version numbers and so on and saves them as object data.
See probe_emacs_version (which uses this internally).
Utilities Used by Initialization
- elisp_to_load_file
-
Given the location of an emacs lisp file, generate the elisp that ensures the library will be available and loaded.
- find_dot_emacs
-
Looks for one of the variants of the user's emacs init file (e.g. "~/.emacs") in the same order that emacs would, and returns the first one found.
Note: this relies on the environment variable $HOME. (This can be changed first to cause this to look for an emacs init in some arbitrary location, e.g. for testing purposes.)
This code does not issue a warning if the elc is stale compared to the el, that's left up to emacs.
- detect_site_init
-
Looks for the "site-start.el" file in the raw load-path without loading the regular emacs init file (e.g. ~/.emacs).
Emacs itself normally loads this file before it loads anything else, so this method replicates that behavior.
Returns the library name ('site-start') if found, undef if not.
- detect_lib
-
Looks for the given elisp library in the load-path after trying to load the given list of context_libs (that includes .emacs as appropriate, and this method uses the requested_load_files as context, as well).
Returns $lib if found, undef if not.
Example usage:
if ( $self->detect_lib("dired") ) { print "As expected, dired.el is installed."; } my @good_libs = grep { defined($_) } map{ $self->detect_lib($_) } @candidate_libs;
- lib_or_file
-
Given the name of an emacs library, examine it to see if it looks like a file system path, or an emacs library (technically a "feature name", i.e. sans path or extension).
Returns a string, either "file" or "lib".
Routines in Use by Some External Libraries
These aren't expected to be generally useful methods, but they are in use by some code (notably Emacs::Run::ExtractDocs).
- elisp_file_from_library_name_if_in_loadpath
-
Identifies the file associated with a given elisp library name by shelling out to emacs in batch mode.
- generate_elisp_to_load_library
-
Generates elisp code that will instruct emacs to load the given library. It also makes sure it's location is in the load-path, which is not precisely the same thing: See "loaded vs. in load-path".
Takes one argument, which can either be the name of the library, or the name of the file, as distinguished by the presence of a ".el" extension on a file name. Also, the file name will usually have a path included, but the library name can not.
Basic Setters and Getters
The naming convention in use here is that setters begin with "set_", but getters have *no* prefix: the most commonly used case deserves the simplest syntax (and mutators are deprecated).
Setters and getters exist for all of the object attributes which are documented with the "new" method (but note that these exist even for "internal" attributes that are not expected to be useful to the client coder).
Special Accessors
- append_to_ec_lib_loader
-
Non-standard setter that appends the given string to the the "elisp_to_load_file" attribute.
- append_to_before_hook
-
Non-standard setter that appends the given string to the the "before_hook" attribute.
Under some circumstances, this module uses the "before_hook" for internal purposes (for -Q and --no-splash), so using an ordinary setter might erase something you didn't realize was there. Typically it's safer to do an append to the "before_hook" by using this method.
accessors that effect the "ec_lib_loader" attribute
If either lib_data or emacs_libs is modified, this must trigger another run of "set_up_ec_lib_loader" to keep the "ec_lib_loader" string up-to-date.
- set_lib_data
-
Setter for lib_data.
- reset_lib_data
-
Reverts lib_data to the value supplied during initization (it empties it entirely, if none was supplied).
Note: this does not (at present) trigger a re-build of "ec_lib_loader", because it's presumed that this will be triggered by some step following this one.
- set_emacs_libs
-
Setter for emacs_libs.
Side effect: runs process_emacs_libs_rest on the given emacs_libs list.
process_emacs_libs_reset indirectly calls set_up_ec_lib_loader so we don't need to do so explicitly here.
- push_emacs_libs
-
Pushes a new lib to the emacs_libs array.
Takes a string, returns aref of the full list of emacs_libs.
Side-effect: runs process_emacs_libs_addition on the new lib, (appending the new info to lib_data).
process_emacs_libs_addition indirectly calls set_up_ec_lib_loader so we don't need to do so explicitly here.
- set_use_shell_directly
-
Setter for object attribute "set_use_shell_directly". If this attribute is set to false, the system will attempt to load IPC::Capture.
Controlling Output Redirection
As described in the documentation for "new", the "redirector" is a code used to control what happens to the output streams STDOUT and STDERR (or in elisp terms, the output from "print" or "message").
These are as defined by the module IPC::Capture: stdout_only, stderr_only or all_output.
The client programmer may not need to worry about the "redirector" at all, since some (hopefully) sensible defaults have been chosen for the major methods here:
all_output (using the object default)
eval_elisp
run_elisp_on_file
stdout_only (special method-level defaults)
get_load_path
get_variable
eval_function
In addition to the "redirector" object attribute, there is a standard method attribute of the same name, which can typically be supplied via an options hash to temporarily override the object "redirector" setting.
If, for example, "eval_elisp" is returning some messages to STDERR that you'd rather filter out, you could do that in one of two ways:
Changing the object-wide default:
my $er = Emacs::Run->new( { redirector => 'stdout_only' } );
my $result = $er->eval_elisp( $elisp_code );
Using an option specific to this method call:
my $er = Emacs::Run->new();
my $result = $er->eval_elisp( $elisp_code, { redirector => 'stdout_only' } );
If you needs some behavior not supported by these redirector codes, it is possible to use a Bourne-shell style redirect, like so:
# return stdout only, but maintain an error log file
my $er = Emacs::Run->new( { shell_output_director => "2>$logfile" } );
my $result = $er->eval_elisp( $elisp_code );
Using the "shell_output_director" (rather than the "redirector") implies shelling out directly, without going through IPC::Capture: this might cause portability issues for some platforms, though these days it should work on most common ones (including Windows).
As with "redirector", there is a "shell_output_director" options hash field as well as an object attribute.
- shell_output_director
-
The shell_output_director (sometimes called sod for short) is a string appended to the internally generated emacs invocation commands to control what happens to output.
Most methods here accept a hash reference of options that can include their own shell_output_director setting.
Typical values (on a unix-like system) are:
- '2>&1'
-
Intermix STDOUT and STDERR (in elisp: both "message" and "print" functions work). This is the default setting for this object attribute.
- '2>/dev/null'
-
return only STDOUT, throwing away STDERR (in elisp: get output only from "print"). But see File::Spec's devnull.
- "> $file 2>&1"
-
send all output to the file $file
- ">> $file 2>&1"
-
append all output to the file $file, preserving any existing content.
- "2 > $log_file"
-
return only STDOUT, but save STDERR to $log_file
MOTIVATION
Periodically, I find myself interested in the strange world of running emacs code from perl. There's a mildly obscure feature of emacs command line invocations called "--batch" that essentially transforms emacs into a lisp interpreter: other command-line options allow one to load files of elisp code and run pieces of code from the command-line.
I've found several uses for this trick. You can use it to:
- to probe your emacs set-up from perl, e.g. for automated installation of elisp using perl tools
- to test elisp code using a perl test harness.
- to use tools written in elisp that you don't want to rewrite in perl (e.g. extract-docstrings.el)
This emacs command line invocation is a little language all of it's own, with just enough twists and turns to it that I've felt the need to write perl routines to help drive the process.
emacs invocation vs Emacs::Run
By default an "emacs --batch" run suppresses most of the usual init files (but does load the essentially deprecated "site-start.pl", presumably for backwards compatibility). Emacs::Run has the opposite bias: here we try to load all three kinds of init files, though each one of these can be shut-off individually if so desired. This is because one of the main intended uses is to let perl find out about things such as the user's emacs settings (notably, the load-path). And in any case, the performance hit of loading these files is no longer such a big deal.
internal documentation (how the code works, etc).
internal attributes
Object attributes intended largely for internal use. The client programmer has access to these, but is not expected to need it. Note: the common "leading underscore" naming convention is not used here.
- ec_lib_loader
-
A fragment of an emacs command line invocation to load emacs libraries. Different attributes exist to specify emacs libraries to be loaded: as these are processed, the ec_lib_loader string gradually accumulates code needed to load them (so that when need be, the process can use the intermediate value of the ec_lib_loader to get the benefit of the previously processed library specifications).
The primary reason for this approach is that each library loaded has the potential to modify the emacs load-path, which may be key for the success of later load attempts.
The process of probing for each library in one of the "requested" lists has to be done in the context of all libraries that have been previously found. Without some place to store intermediate results in some form, this process might need to be programmed as one large monolithic routine.
- lib_data_initial
-
The initial setting of "lib_data" when the object is instantiated. As currently implemented, some operations here require resetting the state of "lib_data" and re-building it. This attribute facilitates that process.
Strategies in Shelling Out
Perl, of course, has some good features for running a shell command and capturing the output, notably qx{} (aka back-quotes).
It's easy enough to append "2>&1" to a shell command when you'd like to see the STDERR messages intermixed with the STDOUT.
This raises fears of portability problems however, which a number of module authors have attempted to address in different ways, my own being IPC::Capture (which optionally uses IPC::Cmd which in turn may use IPC::Run or IPC::Open3). If IPC::Capture is not available, this module will attempt to work by shelling out directly (and similarly, IPC::Capture may also shell out directly using IPC::Cmd only if needed).
This module's methods typically default to capturing all output and returning STDOUT and STDERR intermixed; though unfortunately there is no good way to distinguishing between the messages from STDERR and STDOUT later, and your desired output may be lost in a forest of uninteresting notices sent to STDERR.
From the elisp side, it's important to know that in "--batch" mode, the elisp function "message" sends output to STDERR, and you need to use the elisp function "print" if you'd like to send output to STDOUT. Perhaps unfortunately, the print function also brackets all output with double-quotes and newlines -- the Emacs::Run module compensates by unceremoniously stripping them (via L/<clean_return_value>).
Note: the reasoning behind intermixing STDOUT and STDERR by default is that this batch-mode behavior of message and print is slightly obscure, even to an elisp programmer: returning everything by default is more likely to result in code that Just Works.
A new feature with the 0.3 release is the "shell_output_director" attribute (plus the addition of a method-specific options to override that object-wide suggested default). This provides the user with finer-grained control over how output is handled.
And as of 0.8 an even simpler method is supplied, the "redirector" code.
Loaded vs. in load-path
The emacs variable "load-path" behaves much like the shell's $PATH (or perl's @INC): if you try to load a library called "dired", emacs searches through the load-path in sequence, looking for an appropriately named file (e.g. "dired.el"), it then evaluates it's contents, and the features defined in the file become available for use. It is also possible to load a file by telling emacs the path and filename, and that works whether or not it is located in the load-path.
There is at least a slight difference between the two, however. For example, the present version of the "extract-docstrings.el" library (see Emacs::Run::ExtractDocs) contains code like this, that will break if the library you're looking for is not in the load-path:
(setq codefile (locate-library library))
So some of the routines here (notably "elisp_to_load_file") generate elisp with an extra feature that adds the location of the file to the load-path as well as just loading it.
Interactive vs. Non-interactive Elisp Init
Emacs::Run tries to use the user's normal emacs init process even though it runs non-interactively. Unfortunately, it's possible that the init files may need to be cleaned up in order to be used non-interactively. In my case I found that I needed to check the "x-no-window-manager" variable and selectively disable some code that sets X fonts for me:
;; We must do this check to allow "emacs --batch -l .emacs" to work
(unless (eq x-no-window-manager nil)
(zoom-set-font "-Misc-Fixed-Bold-R-Normal--15-140-75-75-C-90-ISO8859-1"))
The Tree of Method Calls
The potential tree of method calls now runs fairly deep. A bug in a primitive such as "detect_site_init" can have wide-ranging effects:
new
init
append_to_before_hook
process_emacs_libs_addition
set_up_ec_lib_loader
lib_or_file
genec_load_emacs_init
append_to_ec_lib_loader
detect_site_init
detect_lib
genec_loader_lib_needed
append_to_ec_lib_loader
genec_loader_file_needed
quote_elisp
elisp_to_load_file
append_to_ec_lib_loader
genec_loader_lib_requested
detect_lib
append_to_ec_lib_loader
genec_loader_file_requested
quote_elisp
elisp_to_load_file
append_to_ec_lib_loader
probe_emacs_version
parse_emacs_version_string
eval_elisp
quote_elisp
clean_return_value
set_up_ec_lib_loader
lib_or_file
genec_load_emacs_init
append_to_ec_lib_loader
find_dot_emacs
detect_site_init
detect_lib
genec_loader_lib_needed
append_to_ec_lib_loader
genec_loader_file_needed
quote_elisp
elisp_to_load_file
append_to_ec_lib_loader
genec_loader_lib_requested
detect_lib
append_to_ec_lib_loader
genec_loader_file_requested
quote_elisp
elisp_to_load_file
append_to_ec_lib_loader
Note that as of this writing (version 0.09) this code ensures that the "ec_lib_loader" string is up-to-date by continually re-generating it.
TODO
Rather than use elisp's "print", should probably use "prin1" (does what you mean without need for a clean-up routine).
Look into cache tricks (Memoize?) to speed things up a little. See "The Tree of Method Calls".
Have "new" fail (return undef) if emacs can not be found on the system. This way you can use the result of "new" to determine if you should skip tests, etc.
I suspect some quoting issues still lurk e.g. a library filename containing a double-quote will probably crash things.
Add a method to match an emacs regexp against a string. See: http://obsidianrook.com/devnotes/talks/test_everything/bin/1-test_emacs_regexps.t.html
(goto-char (point-min)) (re-search-forward "$elisp_pattern") (setq first_capture (match-string 1))
In "run_elisp_on_file", add support for skipping to a line number after opening a file
I think this feature of emacs invocation could be used to simplify things a little (I'm manipulating load-path directly at present):
`-L DIR' `--directory=DIR' Add directory DIR to the variable `load-path'.
loop in eval_elisp_full_emacs needs to time-out (e.g. if no output is written)
Something like eval_elisp_full_emacs should be able to do image capture of the external emacs process. (Allows automated tests of syntax coloring, etc.)
Write more tests of redirectors -- found and fixed (?) bug in stderr_only.
"make test" is currently showing some annoying subroutine redefined warnings on IPC::Open3 (these don't occur when tests are run individually). Presumably this is related to my funky lashup: IPC::Capture => IPC::Cmd ==> IPC::Run and/or IPC::Open3 Fix/remove the lashup? Try switching to Module::Build?
BUGS & LIMITATIONS
When an elisp library is marked as "needed", and it is not available, failure occurs relatively late: it does not happen during object instantiation, but waits until an attempted run with the object (that is, on a call such as "$er->eval_elisp", not "Emacs::Run->new").
This module was developed around Gnu emacs running on a Gnu/linux platform. Some attempts have been made to make it's use portable to other platforms. At present, using it with a non-gnu emacs such as xemacs is not likely to work.
The "clean_return_value" routine strips leading and trailing newline-quote pairs, but that only covers the case of individual elisp print functions. Evaluating elisp code with multiple prints will need something fancier to clean up their behavior.
"genec_load_emacs_init" runs into trouble if there's a bug in the .emacs file, because it proceeds when it can find the file via "find_dot_emacs", but doesn't verify it will load cleanly: it charges ahead and tries to use it while doing a "detect_lib", which gets confused because it looks for a very specific message to indicate failure, and doesn't understand any error messages from an earlier stage. It would probably be better for "find_dot_emacs" to also vet the file, and error out if it doesn't succeed.
SEE ALSO
Emacs Documentation: Command Line Arguments for Emacs Invocation http://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Invocation.html
A lightning talk about (among other things) using perl to test emacs code: "Using perl to test non-perl code":
http://obsidianrook.com/devnotes/talks/test_everything/index.html
OTHER EXAMPLES
Examples of "advanced" features (i.e. ones you're unlikely to want to use):
# Specify in detail how the emacs lisp libraries should be loaded
# (initialization does not fail if a library that's merely "requested"
# is unavailable):
$lib_data = [
[ 'dired', { type=>'lib', priority=>'needed' } ],
[ '/tmp/my-load-path.el', { type=>'file', priority=>'requested' } ],
[ '/tmp/my-elisp.el', { type=>'file', priority=>'needed' } ],
];
my $er = Emacs::Run->new({
lib_data => $lib_data,
});
my $result = $er->eval_lisp(
qq{ (print (my-elisp-run-my-code "$perl_string")) }
);
# using a "redirector" code (capture only stderr, ignore stdout, like '1>/dev/null')
$er = Emacs::Run->new({
redirector => 'stderr_only'
});
my $result = $er->eval_elisp( '(message "hello world") (print "you can't see me"))' );
# View your emacs load_path from the command-line
perl -MEmacs::Run -le'my $er=Emacs::Run->new; print for @{ $er->get_load_path }';
# Note that the obvious direct emacs invocation will not show .emacs customizations:
emacs --batch --eval "(print (mapconcat 'identity load-path \"\n\"))"
# This does though
emacs --batch -l ~/.emacs --eval "(prin1 (mapconcat 'identity load-path \"\n\"))" 2>/dev/null
AUTHOR
Joseph Brenner, <doom@kzsu.stanford.edu>, 07 Mar 2008
COPYRIGHT AND LICENSE
Copyright (C) 2008 by Joseph Brenner
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.2 or, at your option, any later version of Perl 5 you may have available.
5 POD Errors
The following errors were encountered while parsing the POD:
- Around line 417:
You forgot a '=back' before '=head2'
- Around line 1498:
You forgot a '=back' before '=head2'
- Around line 2078:
'=item' outside of any '=over'
- Around line 2211:
'=item' outside of any '=over'
- Around line 2252:
You forgot a '=back' before '=head2'