Welcome
Maybe in the future I’ll use the site to host some other stuff that I use in my job as a Mac sysadmin
Arjen
make syntax
Published: 2010-01-16 14:32:45
There is some basic stuff you need to know if you want to extend the provided luggage.makefile. I was surprised by the nice structured syntax and by how well it actually fits what you try to do when buiding a package.
Rules
Makefiles are governed by rules, basic syntax is:
target: dependency1, dependency2, etc.
command to create target
optional other command
If the target exists, make skips the rule. The dependencies are also rules, make checks if the dependencies are up-to-date and builds them if necessary. The commands are regular shell commands indented by a tab. make echoes every command before it is executed unless it is preceded with a @. Here’s an example from luggage.make:
payload_d:
@sudo mkdir -p ${PAYLOAD_D}
The target is payload_d, it has no other dependencies and it is built using mkdir -p ${PAYLOAD_D} (${PAYLOAD_D} is a variable declared earlier)
Extended rule syntax
It took some time to understand this one:
unbz2-applications-%: %.tar.bz2 l_Applications
sudo ${TAR} xjf $< -C ${WORK_D}/Applications
@sudo chown -R root:admin ${WORK_D}/Applications/$(shell echo $< | sed s/\.tar\.bz2//g)
It matches all rules that start with unbz2-applications-, using ‘%’ as a wildcard (like * in bash). Then it substitutes the contents of ‘%’ in the first dependency (%.tar.bz2) and then uses the first dependency in the first command using $<.
So if we have a rule that states unbz2-applications-mail.app, make is going to look for a file or rule that is named mail.app.tar.bz2, then it is going to extract that using ${TAR} xjf mail.app.tar.bz2
Syntax
@ prevents the line (not the output) being echoed
- continue after an error occurs
% wildcard, matches any chars
$< matches the first dependency
the Luggage
Published: 2010-01-16 09:30:23
The Luggage is a very clean approach to creating packages. It is written by Joe Block. There are a couple of reasons why you should use this instead of a gui tool like packagemaker, iceberg or composer. [insert reasons here]
There is a good documentation page here: http://luggage.apesseekingknowledge.net/ and a git repository here: http://github.com/unixorn/luggage/
There is some stuff you need to know to get it setup properly, there are some minor bugs in luggage.make which is the driving script and there are some things you need to know to create a package that are not obvious for people not accustomed to Makefiles.
I had to read a couple of tutorials about make to understand what exactly is going on in the script, although it utilizes only a small subset of the available make commands. I put up a a brief tutorial which touches on the used commands.
Requirements
You need an OSX workstation with the Developer tools installed (the luggage needs the packagemaker commandline interface and also ‘make’)
Setup
First, download the zip:
luggage.zip (19.81 KB)I fixed the version that is available on the downloads page which had a typo that prevented it from creating installers for GUI Applications.
Unzip the file.
There are two files that have to go in /usr/local/share/luggage (you have to create this path)
- luggage.make
- prototype.plist
You could put these somewhere else, and change two paths in the Makefiles you are going to create.
Run an example
Just to test that you have everything in place, you could run an example that is provided in the unixorn-luggage-8ef104f directory.
Open a terminal and cd into unixorn-luggage-8ef104f/examples/setup_kerberos
type make pkg
it will prompt you for an admin password if you are not root (luggage uses sudo for all operations)
after some terminal-twitter you will have a timestamped package with a kerberos config file in it.
Amazing isn’t it?
if you type make dmg you’ll get a package wrapped in a dmg ready to deploy on a webserver. I use this to create dmg’s that instaUp2date can download for a build.
Create a Firefox installer
Now we can get to business and create our own package Makefile. It is important to keep in mind that make expects all files to be in the same directory as the Makefile (unless stated otherwise), this was not clear for me in the luggage documentation.
Ok we create a new directory, let’s call it install_firefox.
Download the latest version of Firefox and put it into our new directory.
We’ll have to remove the quarantaine attribute to get rid of the warning:
mcalubook:~ bochoven$ cd Desktop/install_firefox/
mcalubook:install_firefox bochoven$ xattr -d com.apple.quarantine Firefox.app
Now compress the App so it becomes portable:
mcalubook:install_firefox bochoven$ tar cvjf Firefox.app.tar.bz2 Firefox.app
Now in your favorite (plain) texteditor create a new file and copy-paste the following:
include /usr/local/share/luggage/luggage.make
TITLE=install_firefox_app
REVERSE_DOMAIN=com.example.corp
PAYLOAD=unbz2-applications-Firefox.app
Save it as “Makefile” inside install_firefox make sure you uncheck “If no extension is provided, use txt”.
now we return to the terminal and type make pkg
And now we should have a nicely packaged Firefox.app
If you want to try out the package on the machine that you just used to create the package, first do a make clean, otherwise the installer is going to put the package in the fake root that the luggage created. I don’t know why this happens but the make clean fixes this.
Create a better Firefox installer
# This makefile creates a package or a dmg from any application
# that sits next to it
# make pkg
# make dmg
include /usr/local/share/luggage/luggage.make
pack-applications-%: % l_Applications
@sudo ${CP} -R "$<" ${WORK_D}/Applications
@sudo chown -R root:admin ${WORK_D}/Applications/"$<"
@-sudo /usr/bin/xattr -d com.apple.quarantine ${WORK_D}/Applications/"$<"
TITLE := $(shell echo *.app | sed -e 's/\.app//' -e 's/ /_/g')
PACKAGE_NAME=${TITLE}
REVERSE_DOMAIN=com.example.corp
PAYLOAD=pack-applications-*.app
some exlanation
logGen
Published: 2009-03-13 13:20:53
Written by Phil Holland with changes by Dave Pugh, Chris Grieb and J. Billings
logGen is a command-line utility (for now) for detecting filesystem changes after a preference change or package installation. This is primarily useful when creating your own .pkg files so you know what you need to package. 2.2 adds the ability to exclude specific directories from the scanning process in addition to containing logic to avoid logging of resource forks. Please read the updated documentation by typing ‘perldoc /usr/local/sbin/logGen’ .
You can download the logGen installer here: logGen-2.2_.dmg (25.79 KB)
For updates and possible newer versions, please visit
http://www.lsa.umich.edu
Documentation
LOGGEN(1) User Contributed Perl Documentation LOGGEN(1)
NAME
logGen - report filesystem changes
SYNOPSYS
sudo /usr/local/sbin/logGen [--all|-a] [--fast|-f] [--root|-r <dir>]
[--user|-u] [orig.dat]
sudo /usr/local/sbin/logGen [--all|-a] [--fast|-f] [--root|-r <dir>]
[--user|-u] [new.dat] [orig.dat]
sudo /usr/local/sbin/logGen [--all|-a] [--fast|-f] [--root|-r <dir>]
[--user|-u] [new.dat] [orig.dat] > changes.txt
DESCRIPTION
logGen can be used to detect what files have changed as a result of a
configuration change or installing a package. It accomplishes this by
utilizing a number of methods, but mostly using the modification date
and a checksum of each file. Lists will be generated for files that
are added, changed, or deleted, and will include only the directory if
everything within it has been added, changed, or deleted. A number of
directories are automatically ignored in the search including your home
directory, temporary directories, network mounts, and non-root volumes.
As with many tools, logGen cannot accurately detect changes in resource
forks of files.
Before performing any changes or installations you'd like to detect,
take a baseline snapshot of the filesystem by running:
sudo /usr/local/sbin/logGen orig.dat
This will write out a data file (orig.dat) containing a listing of each
file and the information logGen has recorded about each file. This
first pass can taken a very long time (even upwards of 30 minutes)
depending on the speed of your machine and the number of files on your
disk.
Next, make the changes you'd like to detect, such as a preference
change, installing new software, etc, and run logGen a second time. It
is recommended (although not required) to redirect STDOUT to a file for
later examination.
sudo /usr/local/sbin/logGen new.dat orig.dat > changes.txt
This will write out a new data file (new.dat) containing a new, current
listing of each file and the information logGen has recorded about each
file. Next, the data is compared between the new.dat and orig.dat
files and the changes are summarized and printed to STDOUT, and in this
example saved to changes.txt. This second execution of logGen gener-
ally takes much less time than the first.
All of the filenames are changable. If you omit a filename for the
original data file (orig.dat in the above examples) it will default to
<currentEpochTime>.log, such as "1076949440.log".
OPTIONS
Three options are available:
--all (or -a): Check all directories, including /tmp, /var, etc. This
still ignores things like /Network and /Volumes, though, to avoid
checking lots of things you really shouldn't check.
--fast (or -f): Skips MD5 checks of files - the only downside to this
is that files whose timestamps have changed but whose contents remain
identical will be reported as changed even though they didn't.
--root <dir> (or -r <dir>): Sets the root directory of the search to
the specified directory. This could be useful if, for example, you're
just looking for what preference changed in /Library/Preferences/
--user (or -u): Includes the /Users directory in the scan. Normally
this directory is ignored.
The order of the options DOES matter - They should be typed in the same
order as they are listed here. I felt lazy programming that day...
Sorry.
EXAMPLE
% sudo /usr/local/sbin/logGen orig.dat
logGen -- version 1.0
Copyright 2003 - The Regents of the University of Michigan
All Rights Reserved
361883 new files:
---------------
/
---------------
0 changed files
0 deleted files
0 files with resource forks
0 files with special permissions
% sudo /usr/local/sbin/logGen new.dat orig.dat
logGen -- version 1.0
Copyright 2003 - The Regents of the University of Michigan
All Rights Reserved
1 new files:
---------------
/Library/NewDir/
---------------
1 changed files:
---------------
/Library/TestDir2/someFile
---------------
1 deleted files:
---------------
/Library/TestDir/
---------------
1 files with resource forks:
---------------
/Library/iconFile
---------------
2 files with special permissions:
---------------
/Library/aFileSetUID ( setuid )
/Library/anotherFile ( setgid world_writable )
---------------
AUTHORS
Originally written by Phil Holland at the University of Michigan.
Numerous changes provided by Dave Pugh at the University of Michigan.
Questions, requests, comments, and code changes should be sent to
lsa-dev-osx@umich.edu
COPYRIGHT
COPYRIGHT 2005-2008 THE REGENTS OF THE UNIVERSITY OF MICHIGAN ALL
RIGHTS RESERVED
PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDIS-
TRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO
LONG AS NO FEE IS CHARGED, AND SO LONG AS THE COPYRIGHT NOTICE ABOVE,
THIS GRANT OF PERMISSION, AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES
MADE; AND SO LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED
IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR DISTRIBUTION
OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN PRIOR AUTHORIZATION.
THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNI-
VERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WAR-
RANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MER-
CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE
UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR ANY DAMAGES, INCLUDING
SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT
TO ANY CLAIM ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFT-
WARE, EVEN IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
If you do make any changes, it would be appreciated if you submitted
them back to lsa-dev-osx@umich.edu
perl v5.8.6 2008-02-12 LOGGEN(1)
pkgGen
Published: 2009-03-12 23:16:56
pkgGen is a perl script that parses the output from logGen and copies the files to a fake root directory. You can use this tree to build a package using packagemaker, iceberg or some other packaging tool.
The script comes in an installer package that places it in /usr/local/sbin:
pkgGen.zip (7.27 KB)
Source
#!/usr/bin/perl
# pkgGen v1
# Zack Smith
# Based on a script originally created by Geoff Franks (Buy him the Beer)
# Hauptman-Woodward Medical Research Institute
#
# This script takes two parameters. The first is the path to the
# LogGen > log file. The second is the folder which you would like to
# create your Root directory for the PKG installer to base from. This folder
# MUST NOT exist, or the script will fail.
#
# It parses the log file, regenerating the drectory structure of added files
# and directories, preserving permissions, modification times, and resource
# forks as tightly as possible. IMPORTANT: To preserve all permissions, run this
# with root level permissions.
# More to come later...
use strict;
#Set up some globalish variables- charset for the encoding on the log file
#srclog for the log file, root dir for the base directory, and loglines for data
my $CHARSET = "utf-8";
my $SRCLOG;
my $ROOTDIR;
my @loglines;
#function for warn about usage and exit
sub usage{
warn "Usage: pkgGen <path to logGen output txt file> <path of pkg fauxroot>\n";
exit(1);
}
#function for checking arguments
sub getarguments{
#we need arguments
if($#ARGV == -1){
&usage;
exit(0);
#and only 2
}elsif($#ARGV != 1){
&usage;
exit(0);
}
#so if we have them, set the two args
$SRCLOG = shift @ARGV;
$ROOTDIR= shift @ARGV;
#if the log file doesn't exist, quit now.
unless(-e $SRCLOG){
&usage;
exit(0);
}
}
#first line actually run- check the arguments.
&getarguments;
#create the root directory with default perms of root:admin, 1775 (sticky bit)
mkdir($ROOTDIR) or die ("Could not create $ROOTDIR");
chown "root","admin", $ROOTDIR;
chmod oct("1775"), $ROOTDIR;
#open the log file, and read it in
open FILE , "<:encoding($CHARSET)", $SRCLOG or die ("Could not open log file $SRCLOG");
# Split the log file into an array of lines to loop through
@loglines = <FILE>;
close FILE;
#loop through the lines
for my $line (@loglines)
{
#any matches to this unless statement are lines you do not want parsed
# if the script dies due to meaningless filler info like the date, install
# mesages - anything not referring to a file, add them here to be ignored
unless (
$line =~ /(setuid|setgid|sticky_bit|world_writable) \)$/ ||
# ABOVE: Only paths we don't need are theese.
$line =~ /^[^\/](.*)/ ) # Any line that doesn't with a solidus we junk
{
# set up some variables for use within the loop- type for file type, file
# for file path
my $type;
my $file;
if ($line =~ /(.*)\/$/)
{
$type = "dir";
$file = "$1";
# print "Dir: $file\n";
}
else
{
#check to see if it's a file
if ($line =~ /(.*)$/)
{
#set that it's a file, and save the path.
$type = "file";
$file = "$1";
# print "File: $file\n";
}
else
{
#otherwise, I don't know what the line means, so quit and say what
#the line was
print "Unknown line: $line\n";
die("Exiting prematurely...");
}
}
#get an array of directories leading up to the file
my @dirs = split "/" , $file;
my $dir;
my $count = scalar(@dirs);
#loop through all but the file/directory name itself
for (my $i = 0; $i < $count - 1; $i++)
{
#grab the next directory in the array
$dir .= shift @dirs;
$dir .= "/";
#grab file attributes on the real directory
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
$blksize,$blocks) = stat("/$dir");
#translate the mode/type to just the permissions mode
$mode = sprintf("%04o",$mode & 07777);
#if the directory doesn't exist in the recreated structure
unless (-d "${ROOTDIR}${dir}")
{
#make it, setting ownership and perms to the original's
#print "Mode: $mode\n";
#print "mkdir ${ROOTDIR}${dir}"
mkdir("$ROOTDIR/$dir");
chown($uid,$gid,"$ROOTDIR/$dir");
chmod oct($mode), "$ROOTDIR/$dir";
}
}
#by the time we get here, we know the parent directories exist
if ( $type eq "file")
{
#so it's time to copy the file if it's a file
print "Copying $file\n";
# print "cp -vp \"$file\" \"${ROOTDIR}${file}\"\n"
system "cp -p \"$file\" \"${ROOTDIR}${file}\"";
}
else
{
#or if it's a directory,
if ( $type eq "dir")
{
#recreate it with the original perms/ownership
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
$ctime,$blksize,$blocks) = stat("/$file");
print "Creating $file\n";
$mode = sprintf("%o",$mode & 07777);
# print "cp -Rvp \"$file\" \"${ROOTDIR}${file}\"\n"
$file =~ s/\/$//; # Remove trailing solidus for cp sytax
system "cp -Rnp \"$file\" \"${ROOTDIR}${file}\"";
# Recursively copy any new directories
chown($uid,$gid,"${ROOTDIR}${file}");
chmod oct($mode), "${ROOTDIR}${file}";
}
else
{
die( "How the heck did we get here?\n");
}
}
}
}
#after it loops through the log file, that's pretty much it, so it exits.