Sistema de criação Soong

Antes do lançamento do Android 7.0, o Android usava o GNU Make (link em inglês) exclusivamente para descrever e executar regras de criação. O sistema de criação Make tem ampla compatibilidade e é muito utilizado, mas se tornou lento, propenso a erros, não escalonável e difícil de testar no Android. O sistema de compilação Soong oferece a flexibilidade necessária para os builds do Android.

Por isso, os desenvolvedores de plataforma precisam trocar o Make pelo Soong assim que possível. Se precisar de ajuda, envie perguntas para o Grupo do Google android-building.

O que é o Soong?

O sistema de compilação Soong foi introduzido no Android 7.0 (Nougat) para substituir o Make. Ele usa a ferramenta de clonagem do GNU Make, Kati, e o componente de sistema de compilação, Ninja (links em inglês), para acelerar as compilações do Android.

Consulte a descrição do Sistema de compilação Make do Android no Android Open Source Project (AOSP) para ver instruções gerais e mudanças no sistema de compilação para os gravadores do Android.mk e saber mais sobre as modificações necessárias para a adaptação do Make para o Soong.

Consulte as entradas relacionadas ao build no glossário para ver as definições dos termos-chave, e os arquivos de referência Soong para ver todos os detalhes.

Comparação entre o Make e o Soong

Veja uma comparação da configuração do Make com o Soong, fazendo o mesmo em um arquivo de configuração Soong (Blueprint ou .bp).

Exemplo do 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)

Exemplo do Soong

cc_library_shared {
     name: “libxmlrpc++”,

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

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

Consulte Configuração de build simples para ver exemplos de configuração específicos de teste do Soong.

Formato de arquivo Android.bp

Por padrão, os arquivos Android.bp são simples. Eles não contêm condicionais nem instruções de fluxo de controle. Toda a complexidade é processada pela lógica de criação gravada em Go. Quando possível, a sintaxe e a semântica dos arquivos Android.bp são semelhantes aos arquivos Bazel BUILD (link em inglês).

Módulos

Um módulo em um arquivo Android.bp começa com um tipo de módulo seguido por um conjunto de propriedades no formato name: "value",:

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

Cada módulo precisa ter uma propriedade name, e o valor precisa ser exclusivo em todos os arquivos Android.bp, com exceção dos valores de name em namespaces e módulos pré-criados, que podem ser repetidos.

A propriedade srcs especifica os arquivos de origem usados para criar o módulo, como uma lista de strings. Você pode referenciar a saída de outros módulos que produzem arquivos de origem, como genrule ou filegroup, usando a sintaxe de referência ":<module-name>" do módulo.

Para ver uma lista de tipos de módulo válidos e as propriedades deles, consulte a Referência dos módulos Soong.

Tipos

Variáveis e propriedades são fortemente categorizadas, com variáveis baseadas dinamicamente na primeira atribuição e propriedades definidas estaticamente pelo tipo de módulo. Os tipos compatíveis são estes:

  • Boleanos (true ou false)
  • Números inteiros (int)
  • Strings ("string")
  • Listas de strings (["string1", "string2"])
  • Mapas ({key1: "value1", key2: ["value2"]})

Os mapas podem conter valores de qualquer tipo, incluindo mapas aninhados. Listas e mapas podem ter vírgulas à direita do último valor.

Globs

As propriedades que levam uma lista de arquivos, como srcs, também podem ter padrões glob. Os padrões glob podem conter o caractere curinga normal * do UNIX, por exemplo, *.java. Os padrões glob também podem conter um único caractere curinga ** como elemento de caminho, que corresponde a zero ou mais elementos de caminho. Por exemplo, java/**/*.java corresponde aos padrões java/Main.java e java/com/android/Main.java.

Variáveis

Um arquivo Android.bp pode conter atribuições de variáveis de nível superior:

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

As variáveis são definidas para o restante do arquivo em que são declaradas, assim como para quaisquer arquivos Blueprint filhos. As variáveis são imutáveis, com uma exceção: elas podem ser anexadas com uma designação +=, mas somente antes de serem referenciadas.

Comentários

Os arquivos Android.bp podem conter comentários /* */ multilinha no estilo C e de uma única linha // no estilo C++.

Operadores

Strings, listas de strings e mapas podem ser anexados usando o operador +. Os números inteiros podem ser somados usando o operador +. Anexar um mapa gera a união de chaves nos dois mapas, anexando os valores de todas as chaves presentes neles.

Condicionais

O Soong não é compatível com condicionais em arquivos Android.bp. Em vez disso, a complexidade nas regras de criação que exigiriam condicionais é processada no Go, onde os recursos de linguagem de alto nível podem ser usados, e as dependências implícitas introduzidas pelos condicionais podem ser rastreadas. A maioria das condicionais é convertida em uma propriedade de mapa, em que um dos valores no mapa é selecionado e anexado às propriedades de nível superior.

Por exemplo, para compatibilidade com arquivos específicos da arquitetura:

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

Formatador

O Soong inclui um formatador canônico para arquivos Blueprint, semelhante ao gofmt (link em inglês). Para reformatar recursivamente todos os arquivos Android.bp no diretório atual, execute:

bpfmt -w .

O formato canônico inclui recuos de quatro espaços, novas linhas após cada elemento de lista de vários elementos e uma vírgula final em listas e mapas.

Módulos especiais

Alguns grupos de módulos especiais têm características únicas.

Módulos de padrões

Um módulo de padrões pode ser usado para repetir as mesmas propriedades em vários módulos. Por exemplo:

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

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

Módulos pré-criados

Alguns tipos de módulos pré-criados permitem que um módulo tenha o mesmo nome que as contrapartes dele baseadas em origem. Por exemplo, pode haver um cc_prebuilt_binary chamado foo quando já houver um cc_binary com o mesmo nome. Isso dá aos desenvolvedores a flexibilidade de escolher qual versão incluir no produto final. Se uma configuração de criação contiver as duas versões, o valor da sinalização prefer na definição do módulo pré-criado ditará qual versão tem prioridade. Observe que alguns módulos pré-criados têm nomes que não começam com prebuilt, como android_app_import.

Módulos de namespace

Até que o Android seja totalmente convertido de Make para Soong, a configuração de produto Make precisa especificar um valor PRODUCT_SOONG_NAMESPACES. O valor precisa ser uma lista de namespaces separados por espaços, que o Soong exporta para o Make para ser compilada pelo comando m. Os detalhes de ativação de namespaces poderão mudar depois que a conversão do Android para Soong for concluída.

O Soong permite que módulos em diferentes diretórios especifiquem o mesmo nome, contanto que cada módulo seja declarado dentro de um namespace diferente. Um namespace pode ser declarado desta forma:

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

Um namespace não tem a propriedade de nome. O caminho é atribuído automaticamente como o nome dele.

Cada módulo Soong recebe um namespace com base na própria localização na árvore. Considera-se que cada módulo Soong está no namespace definido pelo soong_namespace encontrado em um arquivo Android.bp no diretório atual ou no diretório ancestral mais próximo. Se nenhum módulo soong_namespace for localizado, será considerado que o módulo está no namespace raiz implícito.

Veja um exemplo: o Soong tenta resolver a dependência D declarada pelo módulo M no namespace N que importa namespaces I1, I2, I3...

  1. Então, se D for um nome totalmente qualificado com a forma //namespace:module, somente o namespace especificado será procurado pelo nome do módulo especificado.
  2. Caso contrário, o Soong primeiro procurará por um módulo chamado D declarado no namespace N.
  3. Se esse módulo não existir, o Soong procurará por um módulo chamado D nos namespaces I1, I2, I3…
  4. Por fim, o Soong procurará no namespace raiz.