Sistema de compilación de Soong

Antes 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 se utiliza ampliamente, pero a la escala de Android era lento y propenso a errores, no se podía escalar y resultaba difícil probarlo. El sistema de compilación de Soong ofrece la flexibilidad necesaria para las compilaciones de Android.

Por este motivo, 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 (AOSP) de Android para obtener instrucciones generales. Asimismo, 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.

Para obtener más detalles, 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.

Comparación entre Make y Soong

A continuación, se muestra una comparación de la configuración de Make con Soong que logra 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 contienen condicionales ni declaraciones de flujo de control; toda su complejidad se maneja 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 precompilados, que pueden repetirlo.

La propiedad srcs especifica los archivos de origen utilizados para compilar el módulo como una lista de strings. Puedes hacer referencia al resultado de otros módulos que generan archivos de código fuente, 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 están escritas en negrita; las variables están basadas dinámicamente en la primera asignación y las propiedades están definidas 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 * de comodín de UNIX normal, por ejemplo, *.java. Los patrones glob también pueden contener un solo comodín de ** 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 en el que 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 contener 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én presentes 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 dependencias implícitas con condicionales antepuestos que permiten el seguimiento. La mayoría de los condicionales se convierten en una propiedad de mapa, en la que uno de los valores en el mapa se selecciona y se antepone 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 lo siguiente:

bpfmt -w .
    

El formato canónico incluye cuatro sangrías espaciales, 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 el código 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 su producto final. Si una configuración de compilación contiene 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. Ten en cuenta que 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 producto de Make debe especificar un valor de PRODUCT_SOONG_NAMESPACES. Su 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 para habilitar los espacios de nombres.

Soong permite que los 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"],
    }
    

Ten en cuenta que un espacio de nombres no tiene una propiedad de nombre, sino que su ruta de acceso se asigna automáticamente como su nombre.

Cada módulo de Soong se asigna a un espacio de nombres en función de su 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 superior más cercano. Si no se encuentra este módulo de soong_namespace, se considera que el módulo está en el espacio de nombre de raíz implícito.

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 ese módulo no existe, 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.