Sistema de compilación de Soong

Antes de la actualización de Android 7.0, Android usaba GNU Make de forma exclusiva para describir y ejecutar sus reglas de compilación. El sistema de compilación de Make se admite y utiliza ampliamente, pero, en la escala de Android, era lento y propenso a errores, no se podía escalar y era difícil probar. El sistema de compilación de Soong ofrece la flexibilidad necesaria para las compilaciones de Android.

Por eso, se espera que los desarrolladores de la plataforma dejen de usar Make y adopten Soong lo antes posible. Para recibir asistencia, envía tus preguntas a android-building en Grupos de Google.

¿Qué es Soong?

El sistema de compilación de Soong se introdujo en Android 7.0 (Nougat) para reemplazar a Make. Aprovecha la herramienta de clonación de GNU Make de Kati y el componente del sistema de compilación Ninja para acelerar las compilaciones de Android.

Consulta la descripción del sistema de compilación de Make de Android en el Proyecto de código abierto de Android (AOSP) para obtener instrucciones generales. También consulta los Cambios del sistema de compilación para los escritores de Android.mk a fin de obtener información sobre las modificaciones necesarias para la adaptación de Make a Soong.

Consulta las entradas relacionadas con la compilación en el glosario a fin de conocer las definiciones de los términos clave y los archivos de referencia de Soong para obtener más detalles.

Comparación entre Make y Soong

A continuación, se muestra una comparación de una configuración de Make y una de Soong con los mismos resultados en un archivo de configuración de Soong (Blueprint o .bp).

Ejemplo de Make

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := libxmlrpc++
LOCAL_MODULE_HOST_OS := linux

LOCAL_RTTI_FLAG := -frtti
LOCAL_CPPFLAGS := -Wall -Werror -fexceptions
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/src

LOCAL_SRC_FILES := $(call \
     all-cpp-files-under,src)
include $(BUILD_SHARED_LIBRARY)

Ejemplo de Soong

cc_library_shared {
     name: libxmlrpc++,

     rtti: true,
     cppflags: [
           -Wall,
           -Werror,
           -fexceptions,
     ],
     export_include_dirs: [src],
     srcs: [src/**/*.cpp],

     target: {
           darwin: {
                enabled: false,
           },
     },
}

Consulta la Configuración de compilación simple para obtener ejemplos de configuración de Soong específicos de la prueba.

Formato de archivo Android.bp

Por diseño, los archivos de Android.bp son simples. No tienen condicionales ni declaraciones de flujo de control; toda su complejidad se procesa a través de la lógica de compilación escrita en Go. Cuando es posible, la sintaxis y la semántica de los archivos Android.bp son similares a los archivos BUILD de Bazel.

Módulos

Un módulo en un archivo de Android.bp comienza con un tipo de módulo seguido por un conjunto de propiedades en formato name: "value",:

cc_binary {
    name: "gzip",
    srcs: ["src/test/minigzip.c"],
    shared_libs: ["libz"],
    stl: "none",
}

Cada módulo debe tener una propiedad name, y el valor debe ser único en todos los archivos Android.bp, excepto por los valores de propiedad name en los espacios de nombres y los módulos compilados previamente, que pueden repetirlo.

La propiedad srcs especifica los archivos de origen usados para compilar el módulo como una lista de strings. Puedes hacer referencia a la salida de otros módulos que producen archivos de origen, como genrule o filegroup, mediante la sintaxis de referencia del módulo ":<module-name>".

Si deseas obtener una lista de los tipos de módulos válidos y sus propiedades, consulta la Referencia de los módulos de Soong.

Tipos

Las variables y las propiedades tienen tipado fuerte; las variables se basan de forma dinámica en la primera asignación y las propiedades se definen estáticamente por el tipo de módulo. Los tipos admitidos son los siguientes:

  • Booleanos (true o false)
  • Enteros (int)
  • Strings ("string")
  • Listas de strings (["string1", "string2"])
  • Mapas ({key1: "value1", key2: ["value2"]})

Los mapas pueden contener valores de cualquier tipo, incluidos los mapas anidados. Las listas y los mapas pueden tener comas finales después del último valor.

Globs

Las propiedades que toman una lista de archivos, como srcs, también pueden tomar patrones glob. Los patrones glob pueden contener el comodín * de UNIX normal, por ejemplo, *.java. Los patrones glob también pueden contener un solo comodín ** como un elemento de ruta de acceso, que coincide con cero o más elementos de ruta de acceso. Por ejemplo, java/**/*.java coincide con los patrones java/Main.java y java/com/android/Main.java.

Variables

Un archivo Android.bp puede contener asignaciones de variables de nivel superior:

gzip_srcs = ["src/test/minigzip.c"],
cc_binary {
    name: "gzip",
    srcs: gzip_srcs,
    shared_libs: ["libz"],
    stl: "none",
}

El alcance de las variables se define en función del resto del archivo donde están declaradas, además de los archivos de Blueprint secundarios. Las variables son inmutables, con una excepción: se les puede agregar un sufijo con una asignación +=, pero solo antes de que tengan una referencia.

Comentarios

Los archivos Android.bp pueden tener comentarios /* */ de varias líneas de estilo C y comentarios // de una sola línea de estilo C++.

Operadores

Puedes usar el operador "+" para agregar sufijos a strings, listas de strings y mapas. Los números enteros se pueden resumir con el operador +. Agregar un mapa genera la unión de claves en ambos mapas, y se agregan los valores de cualquier clave que esté presente en ambos mapas.

Condicionales

Soong no admite condicionales en los archivos Android.bp. En cambio, la complejidad en las reglas de compilación que requeriría el uso de condicionales, se administra en Go, que permite usar funciones de lenguaje de alto nivel, y las dependencias implícitas con condicionales antepuestos se pueden seguir. La mayoría de los condicionales se convierte en una propiedad de mapa, donde uno de los valores en el mapa se selecciona y se agrega a las propiedades de nivel superior.

Por ejemplo, para admitir archivos específicos de arquitectura:

cc_library {
    ...
    srcs: ["generic.cpp"],
    arch: {
        arm: {
            srcs: ["arm.cpp"],
        },
        x86: {
            srcs: ["x86.cpp"],
        },
    },
}

Formateador

Soong incluye un formateador canónico para los archivos Blueprint, similar a gofmt. Para volver a formatear de manera recurrente todos los archivos Android.bp en el directorio actual, ejecuta:

bpfmt -w .

El formato canónico incluye sangrías de cuatro espacios, líneas nuevas después de cada elemento de una lista de varios elementos y una coma final en las listas y los mapas.

Módulos especiales

Algunos grupos de módulos especiales tienen características únicas.

Módulos predeterminados

Un módulo predeterminado se puede usar para repetir las mismas propiedades en varios módulos. Por ejemplo:

cc_defaults {
    name: "gzip_defaults",
    shared_libs: ["libz"],
    stl: "none",
}

cc_binary {
    name: "gzip",
    defaults: ["gzip_defaults"],
    srcs: ["src/test/minigzip.c"],
}

Módulos compilados previamente

Algunos tipos de módulos precompilados permiten que un módulo tenga el mismo nombre que sus contrapartes basadas en la fuente. Por ejemplo, puede haber un cc_prebuilt_binary con el nombre foo cuando ya existe un cc_binary con el mismo nombre. Esto les brinda a los desarrolladores la flexibilidad de elegir qué versión incluir en el producto final. Si una configuración de compilación tiene ambas versiones, el valor de la marca de prefer en la definición del módulo compilado previamente determina la versión que tiene prioridad. Algunos módulos compilados previamente tienen nombres que no comienzan con prebuilt, como android_app_import.

Módulos de espacios de nombres

Hasta que Android complete la conversión de Make a Soong, la configuración de productos Make debe especificar un valor de PRODUCT_SOONG_NAMESPACES. El valor debe ser una lista de espacios de nombres separados por espacios que Soong exporta a Make para que el comando m la compile. Una vez que se complete la conversión a Soong de Android, podrían cambiar los detalles de habilitación de los espacios de nombres.

Soong permite que módulos en directorios diferentes especifiquen el mismo nombre, siempre que cada módulo se declare en un espacio de nombre por separado. Un espacio de nombres se puede declarar de la siguiente manera:

soong_namespace {
    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
}

Un espacio de nombres no tiene una propiedad de nombre, sino que se asigna automáticamente la ruta de acceso como su nombre.

Se asigna un espacio de nombre a cada módulo de Soong en función de la ubicación en el árbol. Se considera que cada módulo de Soong está en el espacio de nombre definido por el soong_namespace que se encuentra en un archivo Android.bp en el directorio actual o en el directorio principal más cercano. Si no se encuentra dicho módulo de soong_namespace, se considera que el módulo está en el espacio de nombres de raíz implícito.

Por ejemplo: Soong intenta resolver una dependencia D declarada por el módulo M en el espacio de nombres N que importa los espacios de nombres I1, I2, I3…

  1. Por lo tanto, si D es un nombre completamente calificado del //namespace:module de forma, solo se busca el nombre del módulo especificado en el espacio de nombres indicado.
  2. De lo contrario, Soong primero busca un módulo con el nombre D declarado en el espacio de nombres N.
  3. Si no existe ese módulo, Soong busca un módulo con el nombre D en los espacios de nombres I1, I2, I3…
  4. Por último, Soong busca en el espacio de nombres de raíz.