# MacPorts Internals

This chapter contains information about the MacPorts file layout, configuration files, a few fundamental port installation concepts, and the MacPorts APIs.

## File Hierarchy

## Port Images

MacPorts has a unique ability to allow for multiple versions, revisions, and variants of the same port installed at the same time, so you may test new port versions without uninstalling a previous working version.

This capability derives from the fact that a MacPorts port by default is not installed into its final or "activated" location, but rather to an intermediate location that is only made available to other ports and end-users after an activation phase that makes hard links of all its files in `${prefix}` that point to the port's files in the image repository. Therefore deactivating a port image to install a different version only removes the hard links in `${prefix}` pointing to the previous port version's image —the deactivated port's image is not disturbed.

MacPorts image mode may be turned off in favor of direct mode by setting the portinstalltype variable in the `macports.conf` file "direct". But unless you know what you're doing, don't change it.

## APIs and Libs

The MacPorts system is composed of three Tcl libraries:

- MacPorts API - MacPorts public API for handling Portfiles, dependencies, and registry

- Ports API - API for Portfile parsing and execution

- pextlib - C extensions to Tcl

### Ports API

The code for the Port API is located in `base/src/port1.0`. The Port API provides all the primitives required for a Portfile to be parsed, queried, and executed. It also provides a single procedure call that the MacPorts API uses to kick off execution: "eval_targets". The port Tcl library supplies these procedures, all of which are generated at run-time using the `options` procedure in portutil.tcl.

The macports Tcl library loads the Portfile into a sub-interpreter, within which all port-specific code is run. This process ensures that there will never be pollution of the Tcl space of other ports, nor the MacPorts libraries, nor the calling application.

Portfiles are executed in a Tcl interpreter as Tcl code (and not truly parsed strictly speaking), so every Portfile option must be a TCL procedure.

The Ports API performs the following functions:

- Manages target registrations. All targets register themselves with the Port API. Accordingly, the Port API creates pre-/post-/main overrides for each of the targets.

- Option/Default handling. All Portfile options (name, version, revision ...) are registered by targets. The Port API creates procedures for these options, and sets up the complex variable traces necessary to support option defaults.

- Executes target procedures, including the pre/post/main routines.

- Manages a state file containing information about what variants were specified and what targets have run successfully.

- Provides essential Portfile Tcl extensions (reinplace, xinstall, etc).

- Provides simple access to the ui_event mechanism by providing the various ui_ procedures (i.e., ui_msg, ui_error).

### MacPorts API

The code for the MacPorts API is located in `base/src/macports1.0`. The MacPorts API provides a public API into the MacPorts system by providing simple primitives for handling Portfiles, dependencies, and registry operations, and exports the MacPorts API for the `port` command line utility, or any other. The API has very little information about the contents Portfiles; instead, it relies entirely upon the `port` Tcl library. By keeping the high level API simple and generic, revisions to the underlying ports system will not necessarily require a revision of the high level MacPorts API.

The MacPorts API is also responsible for loading user specified options into a sub-interpreter to be evaluated by the ports API. In that case it sets the variable name in the sub-interpreter and adds the option to the sub-interpreter's global array user_options(). User options are passed as part of the call to `mportopen`.

The MacPorts API performs the following functions:

- Dependency support.

  This is implemented in a highly generic fashion, and is used throughout the system. The dependency functions are exported to the Port API, and the Port API uses them to execute targets in the correct order.

- Dependency processing.

  Software dependencies are handled at this layer using the dependency support layer.

- UI abstractions.

  UI Abstractions are handled at this layer. Each port action is provided a context, and a mechanism for posting user interface events is exported to the Port API (ui_event).

- Registry management routines.

  Manages the rudimentary port registry in `${prefix}/var/macports/receipts/`.

  - `mportregistry::new:` create a new port registry entry.

  - `mportregistry::exists:` check if a port registry entry exists (either versioned or not).

  - `mportregistry::delete:` delete an existing registry entry.

  - `mportregistry::close:` closes a new registry entry.

- Exports the MacPorts API for use by client applications.

  The following routines are defined.

  - `mportinit:` Initializes the MacPorts system. Should be called before trying to use any other procedure.

  - `mportsearch:` Given a regexp, searches the `PortIndex` for ports with matching names.

  - `mportopen:` Given a URI to a port, opens a Portfile and returns an opaque handle to it.

  - `portclose:` Given a port handle, closes a Portfile.

  - `mportexec:` Given a port handle, executes a target (i.e. install).

  - `mportinfo:` Given a port handle, this returns the PortInfo array (as a flat list of array elements). This is a little tricky and unstable and only used by the `portindex` command.

  - `mportdepends:` Given a port handle, returns a list of ports upon which the specified port depends.

For an example of the MacPorts API, when one executes `port search cm3`, the port utility:

- Calls the `mportsearch` function to find all ports containing "cm3".

- Returns Tcl array(s) containing data from the `PortIndex`: port name, version, revision, variants, etc.

- Formats the list of arrays in the standard viewing format.

For another MacPorts API example, when one executes `port install cm3`, the port utility:

- Calls the `mportsearch` function to find the first port that matches the name "cm3".

- Calls the `mportopen` function to open the port.

- Calls the `mportexec` function to execute the install target in the port.

- Calls the `mportclose` function to close the port.

### pextlib

The pextlib TCL library provides a variety of C extensions to add capabilities to TCL procedures; for example, an interface to flock(2) and mkstemp(3).

## The MacPorts Registry

This chapter provides an overview of the MacPorts registry and its API. The registry is queried by MacPorts utilities for information about installed ports related to dependencies, port images, and simple user information about what is installed. It provides abstraction over a modular receipt storage layer; this allows for flat file receipts as well as receipts stored in a SQLite database.

The registry allows MacPorts utilities to:

- Modify receipts to reflect changes made to installed ports being maintained by MacPorts.

- Query the global file and dependency databases for file conflicts between a port being installed and a port already installed.

- Maintain dependency trees of installed ports.

### Registry Files

The flat file registry (MacPorts default registry) files are contained in `${portdbpath}/receipts`, which by default is location `${prefix}/var/macports/receipts`. File mappings and dependency mappings are tracked in the flat file registry by two files:

- file_map.db

- dep_map.bz2

### The Registry API

The MacPorts registry provides a public API in the registry1.0 Tcl package. Using this API listed below you can access the MacPorts Registry using the default receipt storage mechanism chosen in `macports.conf`.

`registry::new_entry {name version {revision 0} {variants ""}}`

  Begin the creation of a new registry entry for the given port. Returns a reference ID to the registry entry created.

`registry::open_entry {name {version 0} {revision 0} {variants ""}}`

  Opens an existing registry entry. Returns a reference ID to the registry entry that was opened.

`registry::entry_exists {name version {revision 0} {variants ""}}`

  Checks to see if a port exists in the registry. Returns 1 if the entry exists, 0 if not.

`registry::write_entry {ref}`

  Writes the receipt associated with the given reference.

`registry::delete_entry {ref}`

  Deletes the receipt associated with the given reference.

`registry::property_store {ref property value}`

  Store the given value with the property name in the receipt associated with the given reference.

`registry::property_retrieve {ref property}`

  Retrieve the property name from the receipt associated with the given reference. Returns the value of the property, if the property exists.

`registry::installed {{name ""} {version ""}}`

  Get all installed ports, optionally all installed ports matching the given name, or the given name and version. Returns a list of the installed ports.

`registry::location {portname portversion}`

  Returns the physical location the port is installed in on the disk. This is primarily useful for finding out where a port image is installed.

`registry::open_file_map {args}`

  Opens the file map that contains file-port relationships.

`registry::file_registered {file}`

  Returns the name of the port that owns the given file, if the file is registered as installed, and 0 otherwise.

`registry::port_registered {name}`

  Returns a list of all files associated with the given port if that port is installed, and 0 otherwise.

`registry::register_file {file port}`

  Registers the given file in the file map as belonging to the given port.

`registry::unregister_file {file}`

  Removes the file from the file map.

`registry::write_file_map {args}`

  Write the changes to the file map.

`registry::open_dep_map {args}`

  Opens the dependency map that contains port dependency relationships.

`registry::fileinfo_for_file {fname}`

  Returns a list for the given file name representing all data currently known about the file. This is a 6-tuple in the form of:

  1. file path

  2. uid

  3. gid

  4. mode

  5. size

  6. md5 checksum

`registry::fileinfo_for_index {flist}`

  Returns a list of information concerning each file in the given file list, if that file exists in the registry. The information if obtained through registry::fileinfo_for_file

`registry::list_depends {name}`

  Returns a list of all the ports that given port name depends on.

`registry::list_dependents {name}`

  Returns a list of all the ports that depend on the given port name.

`registry::register_dep {dep type port}`

  Registers the given dependency as the given type of dependency with the given port.

`registry::unregister_dep {dep type port}`

  Unregister the given dependency of the given type as a dependency of the given port.

`registry::write_dep_map {args}`

  Write changes to the dependency map.