< Scrum | Russ Allbery > Technical Notes | Small-Scale Puppet > |
I read some articles discouraging of the use of DYLDLIBRARYPATH, as the the path of dynamic library should be fixed using -installname, @rpath, and @loaderpath. In terms of making a program that runs both on Linux and Mac OS X, DYLDLIBRARYPATH of Mac OS X does exactly what LDLIBRARYPATH of Linux. For each library that a program uses, the dynamic linker looks for it in each directory in DYLDLIBRARYPATH in turn. If it still can't find the library, it then searches DYLDFALLBACKFRAMEWORKPATH and DYLDFALLBACKLIBRARYPATH in turn. นักพัฒนาที่ย้ายมาใช้ Mac หมาดๆ อย่างพวกเรา อาจจะงงกับการตั้งค่า Environment ต่างๆ บน MacOS ที่ไม่เหมือนกับ Windows มาดูกันว่าเราจะเอา JAVAHOME หรือ PATH ของ Android SDK มาใส่.
It's becoming more and more common these days to link everything againstshared libraries, and in fact many software packages (Tcl and Cyrus SASLcome to mind) basically just don't work properly static. This means thatone has to more frequently deal with the issues involved in finding theappropriate libraries at runtime.
Here's a brief primer on the way that this works on Solaris and Linux.The search paths for libraries come from three sources: the environmentvariable LD_LIBRARY_PATH (if set), any rpath encoded in the binary (moreon this later), and the system default search paths. They're searched inthis order, and the first matching library found is used.
LD_LIBRARY_PATH is broken and should not be used if at all possible. It'sbroken because it overrides the search paths for all binaries that you runusing it, not just the one that you care about, and it doesn't add easilyto other competing settings of LD_LIBRARY_PATH. It has a tendency tocause odd breakage, and it's best to only use it with commercialapplications like Oracle where there's no other choice (and then to set itonly in a wrapper around a particular application, and never in yourgeneral shell environment).
Now, more about the other two mechanisms in detail.
System default paths
Far and away the best way of handling shared libraries is to add everydirectory into which you install shared libraries to the system defaultpaths. This doesn't work if you install a variety of conflictinglibraries, but that's a rare case. If you're just installing softwareinto /usr/local/lib, for example, then just add /usr/local/lib to yoursystem default search paths.
On Linux, you do this by adding those directories to /etc/ld.so.conf andthen running ldconfig. On Solaris, you do this by using the crle
command (see the man page for more details).
This doesn't always work, though. The main case where this doesn't workis when you're installing shared libraries into a shared network filesystem for use throughout your cluster or enterprise. Then, you probablydon't want to add that network file system to the default system searchpath, since that search path is used for every binary on the system,including ones integral to the operation of the system. If the networkfile system goes down, and the default search path includes it, the systemwill become unusable.
That leads to the next approach.
Encoding rpath in applications
ELF binaries (used by Solaris and Linux) can contain in the binarysupplemental paths for shared libraries. (Shared libraries can alsocontain their own supplemental paths for finding other shared libraries,but that's not as commonly used.) This path is searched before the systemdefault paths (but is overridden by LD_LIBRARY_PATH, if set).
This path must be encoded at compile time and after that doesn't change.The advantage of this approach over the LD_LIBRARY_PATH approach, apartfrom the other problems caused by LD_LIBRARY_PATH, is that the work isborn by the person building the software rather than the person runningit. That means the work of configuring library paths only has to be doneonce, by a fairly clued person, rather than by every user who may or maynot understand the issues.
There are two basic ways of telling a compiler to encode a search path inthe binary (called an rpath):
Set the environment variable LD_RUN_PATH to the search path (colon-separated) you want to encode. You don't need to include the system default search directories (and indeed should not, since you may interfere with future changes in how the native library searching algorithm works). This environment variable will be picked up by the compiler when it links the binary.
Add a linker flag such as
-R /usr/local/lib
(or whatever path) to the link command line. You can repeat this flag multiple times with different paths, similar to-L
. Its order in the command line doesn't matter.Different compiler and linker combinations require different flags.
-R
works for many GNU compiler and linker combinations, but not all compilers and linkers. In some cases, you may have to instead tell the compiler to pass the-rpath
flag to the linker. One typical way to do this is with-Wl,-rpath,/usr/local/lib
.
Either can be used, so normally I use LD_RUN_PATH since it's lessintrusive. However, note that if there are any -R
or-Wl,-rpath
flags on the command line, the LD_RUN_PATH setting willtypically be ignored. This means that, if the normal build process of thesoftware adds -R
flags, you'll need to get it to add your -R
flags as well (unless it helpfully includes a -R
flag for theinstallation directory, which it sometimes does). If the software packageuses Autoconf, generally the easiest way to do this is to set LDFLAGS to'-R /usr/local/lib'
(or whatever directory) before runningconfigure, which will then stick it into the Makefile. Failing that, I'llsometimes do something like:
to build the software, which generally does the right thing. (The sameapplies to -Wl,-rpath
flags if you need to use it instead.
Note that software that uses libtool to link its libraries and to linkagainst libraries will more frequently do the right thing and encode rpathproperly when it builds the final binaries, but not always. Thankfullylibtool also understands the -R
and -Wl,-rpath
flags anddoes the right thing when it's provided.
Checking the binaries
To verify that a binary is doing the right thing, use the commandldd
on the binary. This will list all of the libraries and eitherwhere they came from (if found) or something like '(not found)' if thelibrary wasn't found. On Mac OS X, use otool -L
.
Solaris has the very useful -s
option to ldd
that willadditionally show the full library search path, so you can confirm thatyou encoded the right rpath in the binary.
A partial equivalent under Linux is:
but note that this only shows the rpath for a particular binary(executable or library). If a shared library depends on other sharedlibraries, those shared libraries may be searched for using the rpath ofthe shared library that is loading them. To make sure that the searchpath is correct on Linux, you may need to use the above readelf command onthe binary and all libraries other than system libraries that it uses.readelf comes with binutils, so if you're compiling software you willprobably already have it installed.
Changing the rpath
On Linux, a utility called patchelfis available that can modify or remove the rpath or add one if one was notalready present. This utility replaces the older chrpath utility, whichappears to no longer be maintained.
patchelf won't compile on Solaris out of the box. For it, you may stillneed chrpath. The maintainer's FTP site used to be atftp://ftp.hungry.com/pub/hungry/chrpath/ but has been unreachable for sometime. At this point, the best source is probably theDebian package.
Mac OS X comes with a utility named install_name_tool
that can makesimilar modifications to the rpath encoded in a binary. However, it can'tchange the rpath to one that's longer than the original unless the binarywas built with the linker flag -headerpad_max_install_names
.
Other platforms
I'm afraid I don't have as much helpful information about Tru64, HP-UX,AIX, or IRIX. HP-UX and AIX don't use ELF, and therefore have acompletely different linking mechanism. Tru64 and IRIX seem to oftenencode library locations at build time based on where the library wasfound and therefore don't need either of these mechanisms, but I don'tknow exactly how that works. HP-UX and AIX may do something similar.
Note that another option is to just build your software against the staticlibraries. This is what we used to do at Stanford on all platforms otherthan Solaris and Linux (and the BSDs, although I don't normally build onBSD systems), since the handling of shared library versioning anddependencies is considerably less robust on other systems.
< Scrum | Russ Allbery > Technical Notes | Small-Scale Puppet > |
Scriptable System Preferences
Mac OS X Leopard introduces a number of newly scriptable system preference panes, including: Accounts, Appearance, CD & DVD, Desktop, Dock, Expose and Spaces, Network, and Security.
System Preferences Application
It is not necessary to script the System Preferences application to get or set the parameters for scriptable system preferences. Scripting of the preferences is done directly by using the preferences suites included in the System Events application dictionary.
However, occasionally you may wish to launch the System Preferences application and switch to a specific pane or tab for user review or interaction. Here are sample scripts for controlling the System Preference application:
A script for quiting the System Preference application:
-- QUIT SYSTEM PREFERENCCES APPLICATION
if application 'System Preferences' is running then
tell application 'System Preferences' to quit
end if
A script demonstrating how to open a specific system preference pane, how to get the names of the anchors of a pane, and how to switch to a specified anchor (tab):
-- OPEN SYSTEM PREFERENCES TO THE SPACES PANE
tell application 'System Preferences'
activate
set the current pane to pane id 'com.apple.preference.expose'
get the name of every anchor of pane id 'com.apple.preference.expose'
--> returns: {'Main', 'Spaces'}
reveal anchor 'Spaces' of pane id 'com.apple.preference.expose'
end tell
Accounts Preferences
The scripting support in the Accounts Preferences is read-only, meaning you cannot use scripting to generate new users. You can use scripting to set the image used as the current user's picture.
The accounts preferences are read-only except being able to change the picture of the current user:
tell application 'System Events'
-- ACCOUNTS (Read-Only except picture of current user)
get the properties of the current user
--> returns: {class:user, picture path:file 'Mac OS X:Library:User Pictures:Animals:Cat.tif', home directory:file 'Mac OS X:Users:sal:', name:'sal', full name:'Sal Soghoian'}
get the properties of every user
--> returns: {{class:user, picture path:file 'Mac OS X:Library:User Pictures:Flowers:Yellow Daisy.tif', home directory:file 'Mac OS X:Users:sal:', name:'sal', full name:'Sal Soghoian'}}
set the picture path of current user to alias 'Mac OS X:Library:User Pictures:Flowers:Yellow Daisy.tif'
end tell
A script demonstrating how to add login items for the current user account:
tell application 'System Events'
-- LOGIN ITEMS
get the properties of every login item
-- {{class:login item, path:'/Users/sal/Desktop/View Remote Screen.app', hidden:false, kind:'Application', name:'View Remote Screen'}}
-- Adding a login item for the current user
make new login item at end of login items with properties {path:'/Applications/Dictionary.app', hidden:false}
end tell
Appearance Preferences
You can now script the look and functionality of basic user-interface controls.
A script demonstrating how to get and set the appearance preferences.
tell application 'System Events'
tell appearance preferences
get properties
--> returns: {scroll arrow placement:together, font smoothing limit:4, recent applications limit:10, scroll bar action:jump to next page, double click minimizes:true, recent servers limit:10, appearance:blue, recent documents limit:10, highlight color:{46516, 54741, 65535}, class:appearance preferences object, smooth scrolling:false, font smoothing style:automatic}
set properties to {scroll arrow placement:together at top and bottom, font smoothing limit:4, recent applications limit:10, scroll bar action:jump to here, double click minimizes:true, recent servers limit:20, appearance:blue, recent documents limit:20, highlight color:{0, 0, 32000}, smooth scrolling:true, font smoothing style:light}
end tell
end tell
CD & DVD Preferences
Using scripts, you can determine the actions performed when various discs are mounted by the computer.
A script demonstrating how to get and set the properties of inserted disks:
tell application 'System Events'
tell CD and DVD preferences
get the properties of video DVD -- Also: blank CD, blank DVD, music CD, picture CD, video CD
-- > returns: {class:insertion preference, custom script:missing value, insertion action:open application, custom application:file 'Mac OS X:System:Library:CoreServices:Front Row.app:'}
-- OPEN APPLICATION
set properties of video DVD to {insertion action:open application, custom application:'/System/Lirabry/CoreServices/Front Row.app:'}
-- RUN A SCRIPT
set properties of picture CD to {insertion action:run a script, custom script:file 'Mac OS X:Users:sal:Library:Scripts:Import Photo CD.scpt'}
end tell
end tell
Desktop Preferences
By manipulating the scriptable Desktop Preferences, you can control how images are used as desktop backgrounds.
A script demonstrating how to get the properties of the current desktop:
tell application 'System Events'
-- GET DESKTOP PICTURE SETTINGS
tell current desktop
get properties
--> returns: {display name:'Color LCD', random order:false, pictures folder:file 'Mac OS X:Library:Desktop Pictures:', picture rotation:0, class:desktop, change interval:60.0, picture:file 'Mac OS X:Library:Desktop Pictures:Aqua Blue.jpg'}
end tell
end tell
A script demonstrating how to set the desktop to display a folder of images in random sequence:
tell application 'System Events'
-- RANDOM ROTATION OF A FOLDER OF IMAGES
tell current desktop
set picture rotation to 1 -- (0=off, 1=interval, 2=login, 3=sleep)
set random order to true
set pictures folder to file 'Mac OS X:Library:Desktop Pictures:Plants:'
set change interval to 5.0 -- seconds
end tell
end tell
A script demonstrating how to set teh desktop to display a specific picture:
tell application 'System Events'
-- SET DESKTOP TO SPECIFIC PICTURE
tell current desktop
set picture rotation to 0 -- (0=off, 1=interval, 2=login, 3=sleep)
set picture to file 'Mac OS X:Library:Desktop Pictures:Plants:Agave.jpg'
end tell
end tell
Although scripting support is not integrated into the Screen Saver preference, you can use scripting to start the current Screen Saver:
A script to launch the ScreenSaver
-- LAUNCH SCREENSAVER
try
tell application id 'com.apple.ScreenSaver.Engine' to launch
end try
Dock Preferences
You can use AppleScript to control the way the Dock appears in Mac OS X:
A script demonstrating how to get and set the properties of the Dock preferences.
tell application 'System Events'
tell dock preferences
get properties
--> returns: {minimize effect:genie, springing delay:1.0, dock size:0.428571432829, magnification:false, springing:false, location:bottom, class:dock preferences object, magnification size:1.0, animate:true, autohide:false}
set properties to {minimize effect:scale, location:right, autohide:true, magnification:false, magnification size:0.5, dock size:1.0}
end tell
end tell
Expose and Spaces Preferences
Using scripts, you can read and set the values of the Expose and Spaces parameters.
A script demonstrating how to get and set the properties for Expose:
tell application 'System Events'
tell expose preferences
-- SCREEN CORNERS (top right screen corner, bottom left screen corner, bottom right screen corner, top right screen corner)
get the properties of the top right screen corner
--> returns: {activity:show desktop, class:screen corner, modifiers:{}}
set properties of the top right screen corner to {activity:show desktop, modifiers:{control, option}}
-- EXPOSE SHORTCUTS
get the properties of the all windows shortcut
-- {class:shortcut, mouse button:4, function key:F9, function key modifiers:{}, mouse button modifiers:{}}
get the properties of the application windows shortcut
-- {class:shortcut, mouse button:0, function key:none, function key modifiers:{}, mouse button modifiers:{}}
get the properties of the show desktop shortcut
-- {class:shortcut, mouse button:0, function key:F11, function key modifiers:{}, mouse button modifiers:{}}
-- DASHBOARD SHORTCUT
get the properties of the dashboard shortcut
-- {class:shortcut, mouse button:0, function key:none, function key modifiers:{}, mouse button modifiers:{}}
-- SETTING A SHORTCUT
set the properties of the application windows shortcut to {mouse button:3, function key:left control, function key modifiers:{none}, mouse button modifiers:{command}}
end tell
end tell
A script to launch Expose
-- LAUNCH EXPOSE
try
tell application id 'com.apple.exposelauncher' to launch
end try
You can use scripts to determine how Spaces is implemented.
An example script that enables Spaces and sets various parameters:
-- GET AND SET PROPERTIES
tell application 'System Events'
tell expose preferences
tell spaces preferences
set spaces enabled to true
get properties
-- {spaces enabled:true, spaces rows:1, arrow key modifiers:«class
-- SET THE PROPERTIES AND APPLICATION ASSIGNMENTS
set properties to {spaces rows:1, spaces columns:3}
set application bindings to {|com.apple.Mail|:1, |com.apple.AddressBook|:1, |com.apple.Safari|:2, |com.apple.Xcode|:3}
-- SET MODIFIERS OF SWITCHING KEYS
properties of arrow key modifiers
-- {class:spaces shortcut, key modifiers:{control}}
set key modifiers of arrow key modifiers to {command}
properties of numbers key modifiers
-- {class:spaces shortcut, key modifiers:{control}}
set key modifiers of numbers key modifiers to {command}
end tell
end tell
end tell
A script to launch Spaces
-- LAUNCH SPACES
try
tell application id 'com.apple.spaceslauncher' to launch
end try
Network Preferences
Mac Osx Set Library Path
Script the Network preferences is focused on getting and changing the current location setting:
A script demonstrating how to get network properties and the name of the installed set of locations, and how to change the current location:
tell application 'System Events'
tell network preferences
get properties
--> returns: {current location:location id '51BD3FB7-50D1-4859-9649-9138E7FF1ECA' of network preferences, class:network preferences object}
get the name of every location
--> returns: {'Automatic', 'Sprint Card'}
set current location to location 'Automatic'
end tell
end tell
Security Preferences
Basic security preferences can now be read and set with scripts.
A script demonstrating how to get and set the basic computer security settings:
tell application 'System Events'
tell security preferences
get properties
--> returns: {require password to wake:false, class:security preferences object, secure virtual memory:false, require password to unlock:false, automatic login:false, log out when inactive:false, log out when inactive interval:60}
set properties to {require password to wake:false, secure virtual memory:false, require password to unlock:false, automatic login:false, log out when inactive:false, log out when inactive interval:60}
end tell
end tell
Launching Spaces, Expose, Dashboard, and the Screen Saver
In Leopard, you can use scripts to launch Spaces, Expose, Dashboard, or the current screen saver.
A script to launch Spaces
-- LAUNCH SPACES
try
tell application id 'com.apple.spaceslauncher' to launch
end try
A script to launch Expose
-- LAUNCH EXPOSE
try
tell application id 'com.apple.exposelauncher' to launch
end try
A script to launch Dashboard
-- LAUNCH DASHBOARD
try
tell application id 'com.apple.dashboardlauncher' to launch
end try
Mac Osx Set Library Pathway
A script to launch the ScreenSaver
-- LAUNCH SCREENSAVER
try
tell application id 'com.apple.ScreenSaver.Engine' to launch
end try