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 criação Soong (link em inglês) oferece a flexibilidade necessária para as compilações do Android.

O que é o Soong?

O sistema de criação Soong foi introduzido no Android 7.0 (Nougat) para substituir o Make. Esse processo ainda está em andamento.

Consulte a descrição do Sistema de criação Make do Android no Android Open Source Project (AOSP) para ver instruções gerais e fazer alterações no sistema para os gravadores do Android.mk (todos os links em inglês) e saber mais sobre as modificações necessárias para se adaptar do Make ao Soong.

Consulte Configuração de criação simples para ver exemplos de arquivos de configuração Soong (Blueprint ou .bp), além dos arquivos de referência Soong (link em inglês) para ver detalhes completos.

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 em namespaces e módulos pré-criados.

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

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:

  • Booleanos (true ou false)
  • 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 a estes dois caminhos: java/Main.java 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á existir 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á 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.