If I start a custom toplevel in Emacs/tuareg with the tuareg-run-caml function, I need to give the path to the toplevel and the various -I options it requires to find the CMI files. This typing is tedious, is there a more convenient way to start a custom toplevel?
One approach is to add a .ocamlinit in the project directory that uses #directory to add whatever paths are necessary to the toplevel. You can also use this to install printers, add shorter names for commonly used modules, run test code, etc.
Note that you probably want that project specific .ocamlinit to execute ~/.ocamlinit, since things like opam tend to put bits and pieces in there. It might look something like this:
#use "/home/foo/.ocamlinit"
#directory "_build"
open Printf
module V = VeryLongModuleName
Note that #use expects a hard-coded path. This unfortunately interferes with distributing the file.
I further automate this by having an emacs command to start a toplevel that searches the current directory for a file called *.top to execute, falling back to ocaml if none is found. As ocamlbuild provides a fairly simple method of building these files, this avoids much of the tedium of getting a project loaded into a useable toplevel.
Related
I just published my first perl program, unifdef+ (code::unifdefplus, v0.5.3), but I'm not sure if I've done it properly. The program is broken into two parts -- a script (script/unifdef+.pl), and a module (lib/unifdefplus.pm). The script is basically a wrapper for the module. This is supposed to act as a command line utility (which is in reality what I wanted to publish).
The README file I included documents the script, not the module. CPAN seems to be taking the version from the module rather than the script as well (which is undefined at the moment).
So, my questions is: if I want this to be indexed as a script rather than a module, do I need to do anything differently? Also, I'm taking it I should write some documentation for the module as well -- in which case I'm assuming it should be a README file in the lib directory?
Again, I apologize, but this is the first time I've done this, and I want to make sure I've done it right.
Right off the bat, please read On the naming of modules from the PAUSE admins. If you still have questions, or you're still unsure, reach out to modules <at> perl.org.
The simplest way is to use a name in the App:: namespace, such as App::MyMod.
Typically, I'd keep the script and module documentation within their separate files, but near the top of the module documentation, clearly link to the script's documentation, and state that most users will want to read that for normal use.
To build the README from the script documentation:
pod2readme bin/my_script
Likewise, if you change your mind and want README to reference the module instead:
pod2readme lib/App/MyMod.pm
Assuming you're using ExtUtils::MakeMaker for your builds, you can ensure that the script is installed by adding a directive:
EXE_FILES => [
'bin/my_script'
],
With of course your script in the top-level bin directory of your distribution. Other build systems have similar directives.
For example, in my main.scm file I have (load "util.scm"). util.scm is a file in the same folder as main.scm. Both files are located in ~/documents/myproject/.
Now when I'm in this directory, and I run $ chez-scheme main.scm everything works fine. However, if I'm in my home directory and run $chez-scheme documents/myproject/main.scm it complains, not being able to find the file util.scm. I suppose this is the case because the current directory was my relevant home directory, and as such util.scm is indeed not there, it is actually in documents/myproject/. That being said, I'm used (in other languages) to the functionality of looking these paths up relative to the file containing the instruction to import, and I'd like to have that here as well. I've tried prefixing it by ./ or defining the file as a libary and doing (import (util)) but none of it works outside of documents/myproject/. Is there any way to get this to work as I intend it to?
I assume this is Chez-Scheme-specific. If not I'd prefer an answer that is implementation-neutral.
load is kind of awkward in R5RS since the report states that system interfaces are off topic in the report, but they include load which is a half hearted solution. The report does not say if the load is relative to the current directory or the file the load form originates from so in order to be portable I guess you are required to run your script from the current directory and have your loaded file relative to both.
Since Chez Scheme implements R6RS load is not really the right form to use. R6RS removed load in favor of libraries. You should make your file a library and consult how to install it. In some systems that is just placing the files in the right path, adding library location in configuration or running install script. How one uses the library is the same in all implementations, by using import.
According to Chez documentation you can pass --libdirs to it to give it one or more paths to consider for loading libraries. You can see the paths it scans by evaluating (library-directories)
There are several different ways to accomplish what (I think) you are trying to do, but eventually they all boil down to letting Chez know where to look for things. When given relative paths, include and load use the source-directories parameter to search for the requested file. Libraries have their path automatically prepended to source-directories while they are being loaded or compiled, so if your main.scm were a library definition then it would find util.scm as you expect.
However, it sounds like main.scm isn't a library, it's a top-level program. Unfortunately, Chez doesn't have a command line option to set the source-directories like it does for library directories. That leaves you with a bit less flexibility. Any of the following will work:
Make util.scm a library and invoke Chez with the --libdirs option to let it know where to look for libraries.
Set source-directories and load main.scm from inside the REPL rather than from the command line.
Write a wrapper shell script that does the above by echoing the commands into scheme so you don't have to type it yourself. (Only suitable if you don't also need to then type into the scheme session).
Write a wrapper shell script that cds into your project directory before running scheme (and presumably cds back to the original directory when it's done).
I currently have a mess of Perl code that includes something like a configuration.pm file that exports a large number of variables that other modules are using. The same module uses at least one module, call it Foo, which we wrote in some of the helper methods provided by the configuration.pm (they should be in a different module, but not ready to change this yet).
Currently it loads the module with something like this right near the top of the file:
Begin{ push #INC, 'hard/coded/directory'}
use Module::Foo;
I'm trying to get rid of this hard coded directory. I've already added a default configuration file for it to read data from. I moved the import down some and replaced the use with a require, something like this...
$script_directory = $config_data_from_file{'script_directory'};
push #inc, $script_directory;
require Module::Foo;
However, I want to add a command line argument to Main.pl to point to a different configuration file if I don't want to use the default one. My problem is that all the other modules expect configuration.pm to have loaded configuration data and required foo as soon as they include it. So I can't have configuration.pm wait to initialize until main.pl is ready. The closest I can come up with is something like this:
package Configuration;
load_config_file('default/file/location');
sub load_config_file($){
$config_data_from_file = read_file(#_[0]);
$script_directory = $config_data_from_file{'script_directory'};
push #inc, $script_directory;
require Module::Foo;
#load the rest
}
and have Main.pl recall the load_config_file if a command line option changes the configuration file.
But this is a problem for two reasons. First, if my default script location doesn't exist I still explode when I try to do the first import. Second, I'm requiring Foo twice, overwriting it, which could lead to issues if there are difference between the files. For that matter adding the default script_directory to #INC should be avoided.
There are a few ways to fix the problem I could see. A way to more cleanly load different versions of a module to replace the old one, a way to make Foo delay it's attempt to load until the first time it's used in the file, or a way to delay the $load_config_file method until after I read the configuration file for example. However, as a perl newbie I don't know how to do any of them, and haven't had much luck finding out how online.
I actually can do this now, with a fragile order of loading data that makes presumptions or by skipping ahead to a more through refactor of dozens of scripts to implement the long term solution sooner (but I'm really afraid to touch that much code before I have a way to test the code on my computer). However, I'm asking partially in hopes of learning more features of Perl I may find useful later; how would this be solved if I couldn't do the refactoring?
If you want to give the configuration file as the first parameter you can do something like this:
Main script:
#!perl
BEGIN {
use Configuration;
}
use Module::Foo;
... rest of script ...
Configuration.pm:
package Configuration;
load_config_file($ARGV[0] || 'default/file/location');
sub load_config_file($){
$config_data_from_file = read_file(#_[0]);
$script_directory = $config_data_from_file;
push #INC, $script_directory;
}
My solution in general was to look for my -f argument for a configuration file in my configuration.pm as soon as it is loaded and load the configuration file if possible then, while leaving the #ARGV variable untouched so that others could still parse it. This means we end up parsing command line args twice (actually 3 times), but that doesn't do any real harm. I am enforcing the -f argument being predefined in any module that uses my configuration.pm, and sort of require configuration.pm to be the very first module we include, but I consider that a minor expense. Anyone using our configuration.pm file for configuration arguments should desire that behavior.
I found AppConfig was the best module for handling this. My solution could be done without it, but AppConfig made it cleaner because it combined means of loading variables from config file and command line. in fact I, by pure accident, ended up adding the ability to modify any single variable directly from the command line if they choose the way I did it.
My configuration.pm looks something like hits (rewriting this from memory, not exact)
$conf = AppConfig -> new({
GLOBAL=> {
EXPAND => AppConfig::Expand_Var,
ARGCOUNT => AppConfig::ARGCOUNT_ONE
}})
$conf.define("script_dir", {DEFAULT = "/default/location"});
$conf->define("f", {ALIAS ="file|conf_file"});
...other defines here
#read config file if -f arg exists
parse_commandline_args();
$conf->file($conf->conf_file()) if defined $conf->conf_file()
#reread command line so that arguments on it override those in conf file
parse_commandline_args();
#at this point script_dir should be correct so safely include it.
push #INC $conf->script_dir();
sub parse_commandline_args(){
$copy_of_args = [#ARGV];
$conf->args($copy_of_args);
}
My main.pl is practically untouched. I use configuration.pm near the top of the module and everything else just works. I still need to go through and redefine all the scripts that Use a script to require it instead so that configuration.pm has time to update the INC before it runs, but other then that the rest just works. Anywhere I want to use content from the configuration file I now just can $conf->variable()
The parse_commandline_args is important. just using $conf->args() will erase the content of #ARGV, making them unavailable for later modules, like my main.pl. By copying the array first we leave the original #ARGV untouched for later use.
Not sure if I would recommend this from scratch, feels wrong the way configuration.pm is automagically doing everything, but for updating our ugly prototype to function long enough to maintain it until were funded to write the proper version, which I will not be doing in perl, it will do.
Does the LISP program need to be in the same folder as the LISP compiler or can I call it from anywhere?
The basic operation is to call load with a pathname.
(load #p"/home/user710086/foo.lisp")
Then, you may need to run whatever "main" function is supplied by that file.
The location can also be in the current directory, which is, of course, platform dependent. The current directory usually has nothing to do with the directory the Lisp executable resided in, but is the directory of the shell you called it from. I do not know what the current directory is in Windows when you click on something, but I would guess that it is some home-directory-surrogate.
There are several things that may wrap around that basic operation. Usually, code is organized into an ASDF system, and has defined one or more packages. You would then add the .asd file to asdf:*asdf-registry* and then load the package with
(asdf:load-sys 'foo)
This would load all files defined in the .asd file in a calculated order, thus providing you with the system's functionality.
I am currently creating a Perl module for in house use. I used ExtUtils::ModuleMaker to generate a build script and skeleton for my Perl module. I would like to include .ini config files that my core modules need to run properly. Where do I put these files so they are installed with my module? What path do I need to use to access these config files across the main and sub modules?
P.S. this is the directory frame:
|-lib
|---Main.pm
|---Main
|-----subModule1.pm
|-----subModule2.pm
|-----subModule3.pm
|-scripts
|-t
If you are using Module::Install, you can use Module::Install::Share and File::ShareDir.
If you are using Module::Build, you may want to use its config_data tool and and a *::ConfigData module.
Taking a look at the generated Makefile, I would bet the better place to put it is under lib/Main and then you can direct your module to look at ~/.modulerc first, then PERLLIB/Main/modulerc.ini or something like that.
You could also embed the defaults in your module in a way that, in absence of ~/.modulerc, the module works using the default data.
To find the home directory, see File::HomeDir. You'll not want to use ~ (since that's a shell thing anyway).
I would suggest having your module work without the rc file as much as possible. If it doesn't exist, the code should fall back to defaults. This should be true, too, even if the file exists, but a particular flag is missing - it should fall back to the default, too.
You may want to look at Config::Any while you're at it. No point reinventing that wheel.