Utilizzare le API

Developing applications against the Subversion library APIs is fairly straightforward. All of the public header files live in the subversion/include directory of the source tree. These headers are copied into your system locations when you build and install Subversion itself from source. These headers represent the entirety of the functions and types meant to be accessible by users of the Subversion libraries.

Sviluppare applicazioni sopra le API della libreria di Subversion è ragionevolmente diretto. Tutti i file header pubblici sono nella directory subversion/include dell'albero dei sorgenti. Questi header sono copiati nelle posizioni di sistema quando si compila e si installa Subversion stesso dai sorgenti. Questi header rappresentano l'interezza delle funzioni e dei tipi fatti per essere accessibili dagli utenti delle librerie di Subversion.

The first thing you might notice is that Subversion's datatypes and functions are namespace protected. Every public Subversion symbol name begins with svn_, followed by a short code for the library in which the symbol is defined (such as wc, client, fs, etc.), followed by a single underscore (_) and then the rest of the symbol name. Semi-public functions (used among source files of a given library but not by code outside that library, and found inside the library directories themselves) differ from this naming scheme in that instead of a single underscore after the library code, they use a double underscore (__). Functions that are private to a given source file have no special prefixing, and are declared static. Of course, a compiler isn't interested in these naming conventions, but they help to clarify the scope of a given function or datatype.

La prima cosa che si può notare è che i tipi dati di Subversion e le funzioni sono spazi di nomi protetti. Ogni nome di simbolo pubblico di Subversion inizia con svn_, seguito da un codice corto per la libreria nella quale il simbolo è definito (come wc, client, fs, etc.), seguito da un singolo trattino basso (_) e poi dal resto del nome del simbolo. Funzioni parzialmente pubbliche (utilizzate fra file sorgenti di una data libreria ma non dal codice esterno a questa libreria, e si trova all'interno delle directory stesse della libreria) differiscono da questa schema di nomi in quanto anziché di un singolo trattino basso dopo il codice libreria, utilizzano un doppio trattino basso (__). Le funzioni che sono private a un dato file sorgente non hanno prefissi particolari, e sono dichiarate static. Naturalmente, un compilatore non è interessato a queste convenzioni di nomi, ma aiutano a chiarire lo scopo di una data funzione o tipo di dato.

La libreria runtime portatile di Apache

Along with Subversion's own datatypes, you will see many references to datatypes that begin with apr_—symbols from the Apache Portable Runtime (APR) library. APR is Apache's portability library, originally carved out of its server code as an attempt to separate the OS-specific bits from the OS-independent portions of the code. The result was a library that provides a generic API for performing operations that differ mildly—or wildly—from OS to OS. While the Apache HTTP Server was obviously the first user of the APR library, the Subversion developers immediately recognized the value of using APR as well. This means that there are practically no OS-specific code portions in Subversion itself. Also, it means that the Subversion client compiles and runs anywhere that the server does. Currently this list includes all flavors of Unix, Win32, BeOS, OS/2, and Mac OS X.

Oltre ai tipi di dato di Subversion, si vedranno molte referenze a tipi di dato che iniziano con apr_—simboli dalla libreria runtime portabile di Apache (APR). APR è la libreria della portabilità di Apache, originariamente fuoriuscita dal codice del server come un tentativo di separare i bit specifici del sistema operativo dalle porzioni di codice indipendenti dal sistema operativo. Il risultato è stata una libreria che fornisce un'API generica per eseguire operazioni che differiscono leggermente—o molto—da sistema operativo a sistema operativo. Mentre il server HTTP Apache era ovviamente il primo utente della libreria APR, gli sviluppatori di Subversion immediatamente hanno riconosciuto il valore dell'utilizzare APR pure loro. Questo significa che ci sono praticamente porzioni di codice non specifiche di un sistema operativo in Subversion stesso. Inoltre, significa che il client di Subversion compila e si esegue ovunque lo fa il server. Correntemente questa lista include tutti i tipi di Unix, Win32, BeOS, OS/2, e Mac OS X.

In addition to providing consistent implementations of system calls that differ across operating systems, [67] APR gives Subversion immediate access to many custom datatypes, such as dynamic arrays and hash tables. Subversion uses these types extensively throughout the codebase. But perhaps the most pervasive APR datatype, found in nearly every Subversion API prototype, is the apr_pool_t—the APR memory pool. Subversion uses pools internally for all its memory allocation needs (unless an external library requires a different memory management schema for data passed through its API), [68] and while a person coding against the Subversion APIs is not required to do the same, they are required to provide pools to the API functions that need them. This means that users of the Subversion API must also link against APR, must call apr_initialize() to initialize the APR subsystem, and then must acquire a pool for use with Subversion API calls. See sezione chiamata «Programmare con i pool di memoria» for more information.

Oltre a fornire implementazioni consistenti delle chiamate di sistema che differiscono tra i sistemi operativi, [69] APR da a Subversion l'accesso immediato a molti tipi di dato personalizzati, come array dinamici a tabelle hash. Subversion utilizza questi tipi estensivamente attraverso il codice base. Ma forse il più pervasivo tipo di dato di APR, che si trova in quasi ogni prototipo delle API di Subversion, è apr_pool_t—il pool di memoria di APR. Subversion utilizza i pool internamente per tutte le proprie necessità di allocazione di memoria (a meno che una libreria esterna richieda uno schema di gestione della memoria differente per i dati passati attraverso le proprie API), [70] e mentre a una persona che scrive codice con le API di Subversion non è richiesto di fare lo stesso, sono richiesti per fornire i pool alle funzioni delle API che li necessitano. Questo significa che gli utenti delle API di Subversion devono anche collegare l'APR, devono chiamare apr_initialize() per inizializzare il sottosistema APR, e poi devono acquisire un pool per utilizzarlo con le chiamate alle API di Subversion. Vedere sezione chiamata «Programmare con i pool di memoria» per maggiori informazioni.

Requisiti di URL e percorsi

With remote version control operation as the whole point of Subversion's existence, it makes sense that some attention has been paid to internationalization (i18n) support. After all, while remote might mean across the office, it could just as well mean across the globe. To facilitate this, all of Subversion's public interfaces that accept path arguments expect those paths to be canonicalized, and encoded in UTF-8. This means, for example, that any new client binary that drives the libsvn_client interface needs to first convert paths from the locale-specific encoding to UTF-8 before passing those paths to the Subversion libraries, and then re-convert any resultant output paths from Subversion back into the locale's encoding before using those paths for non-Subversion purposes. Fortunately, Subversion provides a suite of functions (see subversion/include/svn_utf.h) that can be used by any program to do these conversions.

Con operazioni remote di controllo di versione come punto centrale dell'esistenza di Subversion, ha senso che qualche attenzione sia stata data al supporto all'internazionalizzazione (i18n). Dopo tutto, mentre «remoto» può significare «attraverso l'ufficio», it could just as well mean «across the globe.» Per facilitare questo, tutte le interfacce pubbliche di Subversion che accettano percorsi come argomenti si aspettano che quei percorsi siano canonizzati, e codificati in UTF-8. Questo significa, per esempio, che ogni nuovo client binario che utilizza l'interfaccia di libsvn_client necessita prima di convertire i percorsi dalla codifica specifica locale a UTF-8 prima passando questi percorsi alle librerie di Subversion, e poi convertendo di nuovo ogni percorso risultante da Subversion indietro alla codifica locale prima di utilizzare questi percorsi per scopi diversi da Subversion. Fortunatamente, Subversion fornisce un insieme di funzioni (vederer subversion/include/svn_utf.h) che possono essere utilizzate da ogni programma per effettuare queste conversioni.

Also, Subversion APIs require all URL parameters to be properly URI-encoded. So, instead of passing file:///home/username/My File.txt as the URL of a file named My File.txt, you need to pass file:///home/username/My%20File.txt. Again, Subversion supplies helper functions that your application can use—svn_path_uri_encode() and svn_path_uri_decode(), for URI encoding and decoding, respectively.

Inoltre, le API di Subversion richiedono tutti i prametri URL di essere propriamente codificati come URI. Così, invece di passare file:///home/username/My File.txt come l'URL di un file di nome My File.txt, occorre passare file:///home/username/My%20File.txt. Ancora, Subversion fornisce funzioni di aiuto che la propria applicazione può utilizzare—svn_path_uri_encode() e svn_path_uri_decode(), per la codifica e la decodifica URI, rispettivamente.

Utilizzare altri linguaggi oltre C e C++

If you are interested in using the Subversion libraries in conjunction with something other than a C program—say a Python or Perl script—Subversion has some support for this via the Simplified Wrapper and Interface Generator (SWIG). The SWIG bindings for Subversion are located in subversion/bindings/swig and whilst still maturing, they are in a usable state. These bindings allow you to call Subversion API functions indirectly, using wrappers that translate the datatypes native to your scripting language into the datatypes needed by Subversion's C libraries.

Se si è interessati ad utilizzare le librerie di Subversion in congiunzione con qualche altro programma non scritto con il C—uno script Python o Perl—Subversion ha qualche supporto per questi attraverso il Simplified Wrapper and Interface Generator (SWIG). I legami di SWIG per Subversion sono posizionati in subversion/bindings/swig e mentre stanno ancora maturando, sono in uno stato usabile. Questi legami permettono di chiamare indirettamente le funzioni delle API di Subversion, utilizzando involucri che traducono i tipi di dato nativi nel linguaggio di script nei tipi di dato necessari alle librerie C di Subversion.

There is an obvious benefit to accessing the Subversion APIs via a language binding—simplicity. Generally speaking, languages such as Python and Perl are much more flexible and easy to use than C or C++. The sort of high-level datatypes and context-driven type checking provided by these languages are often better at handling information that comes from users. As you know, humans are proficient at botching up input to a program, and scripting languages tend to handle that misinformation more gracefully. Of course, often that flexibility comes at the cost of performance. That is why using a tightly-optimized, C-based interface and library suite, combined with a powerful, flexible binding language, is so appealing.

C'è un ovvio beneficio ad accedere alle API di Subversion attraverso un legame di linguaggio—semplicità. Generalmente parlando, i linguaggi come Python e Perl sono molto più flessibili e facili da utilizzare che C o C++. La specie di tipi di dato ad alto livello e il controllo di tipo guidato dal contesto forniti da questi linguaggi sono spesso migliori per la gestione delle informazioni che arrivano dagli utenti. Come si sa, gli esseri umani riescono botching up input a un programma, e i linguaggi script tendono a gestire queste informazioni sbagliate con più garbo. Naturalmente, spesso questa flessibilità viene al costo delle prestazioni. Questo è perché utilizzando una strettamente ottimizzata interfaccia basata sul C e un insieme di librerie, combinate con un potente, flessibile linguaggio di legame, è così interessante.

Let's look at a sample program that uses Subversion's Python SWIG bindings to recursively crawl the youngest repository revision, and print the various paths reached during the crawl.

Vediamo un esempio di programma che utilizza il legame SWIG per Python di Subversion per esplorare ricorsivamente le più recenti revisioni del repository, e stampare i vari percorsi raggiunti durante l'esplorazione.

Esempio 8.2. Utilizzare lo strato repository con Python

#!/usr/bin/python

"""Crawl a repository, printing versioned object path names."""

import sys
import os.path
import svn.fs, svn.core, svn.repos

def crawl_filesystem_dir(root, directory, pool):
    """Recursively crawl DIRECTORY under ROOT in the filesystem, and return
    a list of all the paths at or below DIRECTORY.  Use POOL for all 
    allocations."""

    # Print the name of this path.
    print directory + "/"
    
    # Get the directory entries for DIRECTORY.
    entries = svn.fs.svn_fs_dir_entries(root, directory, pool)

    # Use an iteration subpool.
    subpool = svn.core.svn_pool_create(pool)

    # Loop over the entries.
    names = entries.keys()
    for name in names:
        # Clear the iteration subpool.
        svn.core.svn_pool_clear(subpool)

        # Calculate the entry's full path.
        full_path = directory + '/' + name

        # If the entry is a directory, recurse.  The recursion will return
        # a list with the entry and all its children, which we will add to
        # our running list of paths.
        if svn.fs.svn_fs_is_dir(root, full_path, subpool):
            crawl_filesystem_dir(root, full_path, subpool)
        else:
            # Else it's a file, so print its path here.
            print full_path

    # Destroy the iteration subpool.
    svn.core.svn_pool_destroy(subpool)

def crawl_youngest(pool, repos_path):
    """Open the repository at REPOS_PATH, and recursively crawl its
    youngest revision."""
    
    # Open the repository at REPOS_PATH, and get a reference to its
    # versioning filesystem.
    repos_obj = svn.repos.svn_repos_open(repos_path, pool)
    fs_obj = svn.repos.svn_repos_fs(repos_obj)

    # Query the current youngest revision.
    youngest_rev = svn.fs.svn_fs_youngest_rev(fs_obj, pool)
    
    # Open a root object representing the youngest (HEAD) revision.
    root_obj = svn.fs.svn_fs_revision_root(fs_obj, youngest_rev, pool)

    # Do the recursive crawl.
    crawl_filesystem_dir(root_obj, "", pool)
    
if __name__ == "__main__":
    # Check for sane usage.
    if len(sys.argv) != 2:
        sys.stderr.write("Usage: %s REPOS_PATH\n"
                         % (os.path.basename(sys.argv[0])))
        sys.exit(1)

    # Canonicalize (enough for Subversion, at least) the repository path.
    repos_path = os.path.normpath(sys.argv[1])
    if repos_path == '.': 
        repos_path = ''

    # Call the app-wrapper, which takes care of APR initialization/shutdown
    # and the creation and cleanup of our top-level memory pool.
    svn.core.run_app(crawl_youngest, repos_path)

This same program in C would need to deal with custom datatypes (such as those provided by the APR library) for representing the hash of entries and the list of paths, but Python has hashes (called dictionaries) and lists as built-in datatypes, and provides a rich collection of functions for operating on those types. So SWIG (with the help of some customizations in Subversion's language bindings layer) takes care of mapping those custom datatypes into the native datatypes of the target language. This provides a more intuitive interface for users of that language.

Questo stesso programma in C avrebbe bisogno di trattare con tipi di dato personalizzati (come quelli forniti dalla libreria APR) per rappresentare l'hash delle entrate e la lista dei percorsi, ma Python ha gli hash (chiamati «dizionari») e le liste come tipi di dato interni, e fornisce una ricca collezione di funzioni per operare su questi tipi. Così SWIG (con l'aiuto di qualche personalizzazione nello strato del linguaggio di legame di Subversion) si prende cura di mappare questi tipi di dato personalizzati nei tipi di dato nativi del linguaggio bersaglio. Questo fornisce una più intuitiva interfaccia per gli utenti di questo linguaggio.

The Subversion Python bindings can be used for working copy operations, too. In the previous section of this chapter, we mentioned the libsvn_client interface, and how it exists for the sole purpose of simplifying the process of writing a Subversion client. The following is a brief example of how that library can be accessed via the SWIG bindings to recreate a scaled-down version of the svn status command.

I legami Python di Subversion possono essere anche utilizzati per operazioni sulle copie di lavoro. Nella sezione precedente di questo capitolo, abbiamo menzionato l'interfaccia libsvn_client, e come questa esiste per il solo scopo di semplificare il processo di scrittura di un client di Subversion. Il seguente è un breve esempio di come questa libreria può essere acceduta attraverso i legami SWIG per ricreare una versione ridotta del comando svn status.

Esempio 8.3. Un ricercatore di stato in Python

#!/usr/bin/env python

"""Crawl a working copy directory, printing status information."""

import sys
import os.path
import getopt
import svn.core, svn.client, svn.wc

def generate_status_code(status):
    """Translate a status value into a single-character status code,
    using the same logic as the Subversion command-line client."""

    if status == svn.wc.svn_wc_status_none:
        return ' '
    if status == svn.wc.svn_wc_status_normal:
        return ' '
    if status == svn.wc.svn_wc_status_added:
        return 'A'
    if status == svn.wc.svn_wc_status_missing:
        return '!'
    if status == svn.wc.svn_wc_status_incomplete:
        return '!'
    if status == svn.wc.svn_wc_status_deleted:
        return 'D'
    if status == svn.wc.svn_wc_status_replaced:
        return 'R'
    if status == svn.wc.svn_wc_status_modified:
        return 'M'
    if status == svn.wc.svn_wc_status_merged:
        return 'G'
    if status == svn.wc.svn_wc_status_conflicted:
        return 'C'
    if status == svn.wc.svn_wc_status_obstructed:
        return '~'
    if status == svn.wc.svn_wc_status_ignored:
        return 'I'
    if status == svn.wc.svn_wc_status_external:
        return 'X'
    if status == svn.wc.svn_wc_status_unversioned:
        return '?'
    return '?'

def do_status(pool, wc_path, verbose):
    # Calculate the length of the input working copy path.
    wc_path_len = len(wc_path)

    # Build a client context baton.
    ctx = svn.client.svn_client_ctx_t()

    def _status_callback(path, status, root_path_len=wc_path_len):
        """A callback function for svn_client_status."""

        # Print the path, minus the bit that overlaps with the root of
        # the status crawl
        text_status = generate_status_code(status.text_status)
        prop_status = generate_status_code(status.prop_status)
        print '%s%s  %s' % (text_status, prop_status, path[wc_path_len + 1:])
        
    # Do the status crawl, using _status_callback() as our callback function.
    svn.client.svn_client_status(wc_path, None, _status_callback,
                                 1, verbose, 0, 0, ctx, pool)

def usage_and_exit(errorcode):
    """Print usage message, and exit with ERRORCODE."""
    stream = errorcode and sys.stderr or sys.stdout
    stream.write("""Usage: %s OPTIONS WC-PATH
Options:
  --help, -h    : Show this usage message
  --verbose, -v : Show all statuses, even uninteresting ones
""" % (os.path.basename(sys.argv[0])))
    sys.exit(errorcode)
    
if __name__ == '__main__':
    # Parse command-line options.
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hv", ["help", "verbose"])
    except getopt.GetoptError:
        usage_and_exit(1)
    verbose = 0
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage_and_exit(0)
        if opt in ("-v", "--verbose"):
            verbose = 1
    if len(args) != 1:
        usage_and_exit(2)
            
    # Canonicalize (enough for Subversion, at least) the working copy path.
    wc_path = os.path.normpath(args[0])
    if wc_path == '.': 
        wc_path = ''

    # Call the app-wrapper, which takes care of APR initialization/shutdown
    # and the creation and cleanup of our top-level memory pool.
    svn.core.run_app(do_status, wc_path, verbose)

Subversion's language bindings unfortunately tend to lack the level of attention given to the core Subversion modules. However, there have been significant efforts towards creating functional bindings for Python, Perl, and Ruby. To some extent, the work done preparing the SWIG interface files for these languages is reusable in efforts to generate bindings for other languages supported by SWIG (which includes versions of C#, Guile, Java, MzScheme, OCaml, PHP, Tcl, and others). However, some extra programming is required to compensate for complex APIs that SWIG needs some help interfacing with. For more information on SWIG itself, see the project's website at http://www.swig.org/.

I legami ai linguaggi di Subversion sfortunatamente tendono a a difettare nel livello di attenzione dato ai moduli del cuore di Subversion. Comunque, ci sono stati sforzi significati verso la creazione di legami funzionali per Python, Perl, e Ruby. In parte, il lavoro fatto preparando i file dell'interfaccia di SWIG per questi linguaggi è riusabile nello sforzo di generare legami per altri linguaggi supportati da SWIG (che includono versioni di C#, Guile, Java, MzScheme, OCaml, PHP, Tcl, e altri). Tuttavia, qualche programmazione in più è richiesta per compensare le API complesse così che SWIG necessita di qualche aiuto per interfacciarsi. Per maggiori informazioni su SWIG stesso, vedere il sito web del progetto a http://www.swig.org/.



[67] Subversion uses ANSI system calls and datatypes as much as possible.

[68] Neon and Berkeley DB are examples of such libraries.

[69] Subversion utilizza chiamate di sistema ANSI e tipi di dato il più possibile.

[70] Neon e Berkeley DB sono esempi di questo tipo librerie.