Java Jar file manifests, and Class-Path

I've been having trouble building jar files. I'm using Apache ant on Mac OS X. I have been unable to create valid Class-Path values when using ant to generate my MANIFEST.MF files.

The solution: compose my MANIFEST.MF file manually (based on the broken MANIFEST.MF created by ant). It turns out that Class-Path can be specified as a series of space-separated paths extending across multiple lines, with leading indentation. F'rinstance:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.4
Created-By: 1.4.2-50 ("Apple Computer, Inc.")
Main-Class: JChemLoader
Class-Path: /usr/local/jchem/lib/batik-core.jar 
 /usr/local/jchem/lib/chart.jar /usr/local/jchem/lib/dom4j.jar 
 /usr/local/jchem/lib/jchem.jar 
 /usr/local/lib/java/postgresql.jdbc.jar

As long as no single Class-Path entry is more than 70-71 characters in length, this should work.

The problem with ant-generated Class-Path values was that the values, when written out, were being split at arbitrary points. (The value I was supplying to ant was a colon-separated sequence of pathnames. Maybe that was part of my problem?) ant was trying to satisfy the rules for jar file formatting, as specified by Sun:

Line length: No line may be longer than 72 bytes (not characters), in its UTF8-encoded form. If a value would make the initial line longer than this, it should be continued on extra lines (each starting with a single SPACE).

Perhaps it's just my lack of familiarity, but I find Java, jar files, and CLASSPATH to be a vile combination. Python (PYTHONPATH) handles things much more cleanly. Even gcc and make handle library locations in a much more pleasant way.

Passing arguments to Mac applications

I'm building a suite of cooperating, cross-platform GUI applications. They're written in C++, using Qt. I'd like to be able to pass command-line arguments from one app to another, but on OS X I've had trouble doing so.

Mac GUI applications typically are launched via NSWorkspace (or, for non-Cocoa apps like the ones I'm building, via the underlying CoreFoundation functions). They're launched with no argv entries. Arguments typically consist only of pathnames to open or print, etc. Accessing them is no picnic.

The simplest solution I've found uses environment variables to pass the arguments around. It works on Unixes including OS X. It should work on Windows.

One gotcha is that you have to explicitly retrieve the environment arguments when your GUI app starts up, instead of having them initialized for you by the C/C++ runtime.

Another gotcha is namespace collisions. The names of the environment variables conveying the arguments must be chosen carefully to prevent them being used/modified by unrelated applications. Maybe some sort of Java-like "com.dmoonc.my_app_suite..." convention would be appropriate.

Anyway, this is probably good enough for now:

envargs.h

#ifndef ENVARGS_H
#define ENVARGS_H

#include <string>
#include <vector>

typedef std::vector< std::string > ArgList;

// Retrieve and clear the environment arguments
// passed to this process.
// Should be done as soon as possible 
// after application launch.
void getEnvArgs(ArgList& args);
ArgList envArgs();

// Set environment arguments for passing 
// to a process about to be launched.
void setEnvArgs(ArgList& newArgs);

#endif // ENVARGS_H

envargs.cpp

#include "envargs.h"
#include <iostream>
#include <sstream>
#include <stdlib.h>

using namespace std;

static const string C_NumArgsName("_appsuite_numargs");
static const string C_ArgPrefix("_appsuite_arg_");

void getEnvArgs(ArgList& args)
{
    args.clear();

    char *numArgsCStr = getenv(C_NumArgsName.c_str());
    if (numArgsCStr)
    {
        istringstream ins(numArgsCStr);
        int numArgs;
        if (ins >> numArgs)
        {
            for (int i = 0; i < numArgs; ++i)
            {
                ostringstream argname;
                argname                 
                const char *argnameCStr = argname.str().c_str();
                char *argCStr = getenv(argnameCStr);
                if (argCStr)
                {
                    string arg(argCStr);
                    args.push_back(arg);
                }
                else
                {
                    args.push_back("");
                }
            }
        }
    }
}

ArgList envArgs()
{
    ArgList result;
    getEnvArgs(result);
    return result;
}

static bool cppsetenv(string name, string value)
{
    bool result = true;
    ostringstream spec;
    spec     
    result = (0 == putenv((char *)(spec.str().c_str())));
    if (!result)
    {
        cerr         // TO DO:  Ditch the bool return value and throw an exception
    }
    return result;
}

static bool cppsetenv(string name, unsigned int ivalue)
{
    ostringstream svalue;
    svalue     return cppsetenv(name, svalue.str());
}

void setEnvArgs(ArgList& newArgs)
{
    if (cppsetenv(C_NumArgsName, newArgs.size()))
    {
        for (unsigned int i = 0; i < newArgs.size(); ++i)
        {
            ostringstream name;
            name             cppsetenv(name.str(), newArgs[i]);
        }
    }
}

RSS News Readers: Extrapolation

This claim is false:

"When people get an RSS feed, they need not go through standard Web site navigation patterns that we are accustomed to tracking. They look at just what they want, when they want."

The same claim could be made for the early web. The problem is not looking at what you want. The problem is finding what you want to look at.

What (I Hope) Is Next For RSS News Readers


RSS news readers help you find and read content which is of interest to you. They face challenges.

RSS Advertisements

News readers will become less useful if they cannot help you filter out RSS advertisements.

Redundant Articles

I already wish news readers could perform higher-order aggregation. My subscriptions reflect my interests, so they have significant content overlap. Just this morning, for example, I must have seen about ten different news items all providing roughly the same information about Mighty Mouse.

I wish my news reader could at least identify and group together "duplicate" articles such as these, so I could deal with them all at once. Click on one to read it, mark the others en masse as having been read, move on.

I wish I could use my system's standard search tools (Spotlight for me, who knows what for Longhorn^WVista users) to search through old feed articles.

Not to be a shill for Brent Simmons, but the full version of NetNewsWire seems to have begun to address these wishes.

It's nice that my most original ideas have already been had by others.

Managing Finder Workflows

With the new Automator in Mac OS X 10.4 it's easy to add contextual menu items to the Finder. Just edit your workflow in Automator, Select "File->Save As Plug-in...", enter a name for the plug-in, and specify that the plug-in is for Finder. (Apple has posted some examples of how to create plug-ins.)

It's easy to add a workflow menu item. How do you remove one?

Intuition says that your personal workflows might be represented as files somewhere in your home directory. Finder's new Smart Folders should tell whether that's really the case.

Create a dummy workflow named "DeleteMe" and save it as a Finder Plug-in. Then switch to the finder and select "File->New Smart Folder". In the little brushed-metal "toolbar" which appears, select the "Home" folder. In the criteria list add the following criteria:

"Kind", "Any", "+"
"Last Modified", "Today"

Somewhere in the list of search results a Folder named "Finder" should appear. Hitting the info icon on that item doesn't tell much, but the "More Info..." button reveals that it's located in /Users/YourUsername/Library/Workflows/Applications/Finder. And lo, opening the folder shows an Automator Workflow named "DeleteMe".

Drag the workflow to the trash, right click anywhere in a Finder window, and select the "Automator" cascading menu item. The "DeleteMe" menu item should be gone.

The Short Form

Workflow menu items live in your ~/Library/Workflows/Applications/ folders. They can be removed from your context menus by dragging them to the trash.

There's probably a better way to discover all of this...

Installing PyOpenGL on Mac OS X 10.4.1

Update 2007/02/26

Mike Fletcher has been working on PyOpenGL 3, which uses ctypes instead of swig. As a result, installation and updating are a lot easier. See the SourceForge project page for more info, but here's a quick summary:

$ sudo easy_install PyOpenGL
Password:
Searching for PyOpenGL
Reading http://cheeseshop.python.org/pypi/PyOpenGL/
Reading http://pyopengl.sourceforge.net/ctypes/
Reading http://sourceforge.net/project/showfiles.php?group_id=5988
Reading http://cheeseshop.python.org/pypi/PyOpenGL/3.0.0a5
Best match: PyOpenGL 3.0.0a6
Downloading http://downloads.sourceforge.net/pyopengl/PyOpenGL-3.0.0a6.zip?modtime=1171556482&big_mirror=0
Processing PyOpenGL-3.0.0a6.zip
Running PyOpenGL-3.0.0a6/setup.py -q bdist_egg --dist-dir /tmp/easy_install-9bs-Ie/PyOpenGL-3.0.0a6/egg-dist-tmp-1Y8vWy
Adding PyOpenGL 3.0.0a6 to easy-install.pth file

Installed /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/PyOpenGL-3.0.0a6-py2.5.egg
Processing dependencies for PyOpenGL

You can also install and run the demos:

$ sudo easy_install PyOpenGL-Demo

Once they're installed, how do you get at them?

$ cd /Library/Frameworks/Python.framework/Versions/Current/lib/python2.5/site-packages/PyOpenGL_Demo-3.0.0a6-py2.5.egg/PyOpenGL-Demo/
$ cd redbook
$ python movelight.py

Each demo pops up in its own lovely, top-level window.

Original Post

After upgrading to Mac OS X 10.4.1 I decided to install Python 2.4.1. Python made a framework install with no problem. But I ran into a small problem trying to install PyOpenGL-2.0.1.09.

I kept getting compile errors complaining about an incomplete definition for st_atimespec, et al. (Here comes another batch of extremely long lines...)

In file included from /Library/Frameworks/Python.framework/Versions/2.4/include/python2.4/pyport.h:155,
                 from /Library/Frameworks/Python.framework/Versions/2.4/include/python2.4/Python.h:55,
                 from src/config.h:1,
                 from _configtest.c:6:
/System/Library/Frameworks/Kernel.framework/Headers/sys/stat.h:225: error: field 'st_atimespec' has incomplete type
/System/Library/Frameworks/Kernel.framework/Headers/sys/stat.h:226: error: field 'st_mtimespec' has incomplete type
/System/Library/Frameworks/Kernel.framework/Headers/sys/stat.h:227: error: field 'st_ctimespec' has incomplete type

What was odd was that the build process was trying to include sys/stat.h from .../Kernel.framework/Headers, instead of from /usr/include.

I modified config/darwin.cfg and removed /System/Library/Frameworks/Kernel.framework/Headers from the list of include directories, and thereafter PyOpenGL built and installed without error.

The examples ran, too :)

Here's the context diff for config/darwin.cfg. Apologies, again, for the long lines -- I've tried to escape them using trailing backslashes.

*** darwin.cfg.orig Sat Jun 11 12:09:35 2005
--- darwin.cfg.new Sat Jun 11 12:19:42 2005
***************
*** 11,17 ****
  [General]
  build_togl=0
  gl_platform=CGL
! include_dirs=/System/Library/Frameworks/OpenGL.framework/Headers:/System/Library/Frameworks/GLUT.framework/Headers:/System/Library/Frameworks/Kernel.framework/Headers
  # Bob's OSX patch, comments out the library directories, adds -framework:GLUT
  # for the *general* build parameters?  Is that actually needed?
  ; Should make a 'frameworks= ' option!
--- 11,17 ----
  [General]
  build_togl=0
  gl_platform=CGL
! include_dirs=/System/Library/Frameworks/OpenGL.framework/Headers:/System/Library/Frameworks/GLUT.framework/Headers
  # Bob's OSX patch, comments out the library directories, adds -framework:GLUT
  # for the *general* build parameters?  Is that actually needed?
  ; Should make a 'frameworks= ' option!

Trac, clearsilver and Python 2.4.1

Trac is an excellent web-based issue tracking system. It depends on clearsilver.

Clearsilver 0.9.14 doesn't like to configure with Python 2.4, due to changes in the internals of Python's site module.

The fix is easy enough, but for some reason it's hard to find via Google. (You can find it by searching for Python 2.4 within clearsilver's Yahoo Groups discussion forum.) Here's a context diff for clearsilver's configure script, showing the fix that worked for me. Apologies for the long lines...

*** configure   Tue May 24 16:28:58 2005
--- configure.orig      Tue May 24 16:24:45 2005
***************
*** 3084,3091 ****
      PYTHON=$python_bin
      PYTHON_INC="-I$python_inc"
      PYTHON_LIB=$python_lib
!     PYTHON_SITE=`$python_bin -c "import sys, os; print os.path.join(sys.prefix, 'lib', 'python%s' % sys.version[:3], 'site-packages')"`
! #    PYTHON_SITE=`$python_bin -c "import site; print site.sitedirs[0]"`
      BUILD_WRAPPERS="$BUILD_WRAPPERS python"
    fi
  fi
--- 3084,3090 ----
      PYTHON=$python_bin
      PYTHON_INC="-I$python_inc"
      PYTHON_LIB=$python_lib
!     PYTHON_SITE=`$python_bin -c "import site; print site.sitedirs[0]"`
      BUILD_WRAPPERS="$BUILD_WRAPPERS python"
    fi
  fi

Here is the relevant discussion thread.