O que ENABLE_BITCODE faz no xcode 7?


262

Estou com um problema com o termo de código de bits incorporado.
O que é código de bits incorporado?
Quando ativar, ENABLE_BITCODEno novo Xcode?
O que acontece com o binário quando ativado, ENABLE_BITCODEno Xcode 7?

Respostas:


312

Código de bits refere-se ao tipo de código: "Código de bits LLVM" enviado ao iTunes Connect. Isso permite que a Apple use certos cálculos para otimizar ainda mais os aplicativos (por exemplo: tamanhos de executáveis ​​possivelmente reduzidos). Se a Apple precisar alterar seu executável, poderá fazer isso sem que uma nova compilação seja carregada.

Isso difere de: Fatiar, que é o processo da Apple para otimizar seu aplicativo para o dispositivo de um usuário com base na resolução e na arquitetura do dispositivo. O fatiamento não requer Bitcode. (Por exemplo: incluindo apenas imagens @ 2x nos 5s)

O App Thinning é a combinação de fatia, código de bits e recursos sob demanda

Bitcode é uma representação intermediária de um programa compilado. Os aplicativos enviados para o iTunes Connect que contêm código de bits serão compilados e vinculados na App Store. A inclusão de código de bits permitirá que a Apple otimize novamente o binário do seu aplicativo no futuro, sem a necessidade de enviar uma nova versão do seu aplicativo para a loja.

Documentação da Apple sobre o afinamento de aplicativos


Nada do que você citou diz que ativar o código de bits diminui o tamanho do aplicativo no dispositivo do usuário. O código de bits não tem nada a ver com recursos como 3x ou 2x.
user102008

1
Novamente, os recursos não têm nada a ver com o Bitcode, que é sobre o código. O download por usuários de apenas certas arquiteturas de código e certas versões de recursos é o Slicing, que nada tem a ver com o Bitcode.
user102008

7
Não concordo que ele permita que a Apple reduza o tamanho do seu aplicativo. Em nenhum lugar diz isso. Ele diz que "permitirá que a Apple otimize novamente o binário do seu aplicativo no futuro, sem a necessidade de enviar uma nova versão do seu aplicativo para a loja", o que entendo que isso permita que a Apple recompile seu aplicativo para uma nova arquitetura se sai um novo dispositivo com uma nova arquitetura, sem a necessidade de enviar uma nova versão que inclua essa arquitetura.
user102008

2
Não, o fatiamento está separando os recursos de seus aplicativos em grupos para dispositivos específicos. O código de bits é o que permite à Apple gerar um executável para uma arquitetura específica.
Jon Shier

2
A @JonShier Apple diz que "fatiar é o processo de criar e fornecer variantes do pacote de aplicativos para diferentes dispositivos de destino. Uma variante contém apenas a arquitetura executável e os recursos necessários para o dispositivo de destino". Portanto, fatiar é ter apenas o código e os recursos executáveis para um determinado dispositivo.
21415 KJi-

80

O que é código de bits incorporado?

De acordo com os documentos :

Bitcode é uma representação intermediária de um programa compilado. Os aplicativos enviados para o iTunes Connect que contêm código de bits serão compilados e vinculados na App Store. A inclusão de código de bits permitirá que a Apple otimize novamente o binário do seu aplicativo no futuro, sem a necessidade de enviar uma nova versão do seu aplicativo para a loja.

Atualização: esta frase em "Novos recursos no Xcode 7" me fez pensar por um longo tempo que o Bitcode é necessário para o Slicing para reduzir o tamanho do aplicativo:

Quando você arquiva para envio à App Store, o Xcode compila seu aplicativo em uma representação intermediária. A App Store compilará o código de bit nos executáveis ​​de 64 ou 32 bits, conforme necessário.

No entanto, isso não é verdade, o Bitcode e o Slicing funcionam independentemente: Slicing consiste em reduzir o tamanho do aplicativo e gerar variantes de pacote de aplicativos, e o Bitcode trata de certas otimizações binárias. Verifiquei isso verificando as arquiteturas incluídas nos executáveis ​​de aplicativos que não são de código de bits e constatando que eles incluem apenas os necessários.

O Bitcode permite que outro componente do App Thinning chamado Slicing gere variantes de pacotes de aplicativos com executáveis ​​específicos para arquiteturas específicas, por exemplo, a variante do iPhone 5S incluirá apenas o executável arm64, o iPad Mini armv7 e assim por diante.

Quando ativar ENABLE_BITCODE no novo Xcode?

Para aplicativos iOS, o código de bits é o padrão, mas opcional. Se você fornecer código de bits, todos os aplicativos e estruturas no pacote de aplicativos precisarão incluir código de bits. Para aplicativos watchOS e tvOS, é necessário um código de bits.

O que acontece com o binário quando ENABLE_BITCODE está ativado no novo Xcode?

Da referência do Xcode 7:

A ativação dessa configuração indica que o destino ou projeto deve gerar código de bits durante a compilação para plataformas e arquiteturas que o suportam. Para compilações de arquivo morto, o código de bit será gerado no binário vinculado para envio à loja de aplicativos. Para outras compilações, o compilador e o vinculador verificarão se o código está em conformidade com os requisitos para geração de código de bit, mas não gerará código de bit real.

Aqui estão alguns links que ajudarão a entender melhor o Bitcode :


O código de bits será incluído se eu tiver ENABLE_BITCODE, mas desmarque a opção "Incluindo código de bits" antes de enviar para a App Store?
allaire

"Para aplicativos iOS, o código de bits é o padrão, mas opcional." Hã..? Volte novamente..? É OU não é opcional ..?
precisa saber é o seguinte

@ NpC0mpl3t3, conforme indicado na resposta, é opcional para aplicativos iOS, mas necessário para aplicativos watchOS e tvOS.
Maxim Pavlov

Excelente ajuda! Esta resposta aqui mostra como desabilitar o código de bits: stackoverflow.com/a/41418824/9190
Guerry

20

Como a pergunta exata é "o que permite o código de bits", eu gostaria de fornecer alguns detalhes técnicos que eu descobri até agora. É praticamente impossível descobrir com 100% de certeza até que a Apple libere o código-fonte desse compilador

Primeiro, o código de bits da Apple não parece ser a mesma coisa que o bytecode do LLVM. Pelo menos, não consegui descobrir nenhuma semelhança entre eles. Parece ter um cabeçalho proprietário (sempre começa com "xar!") E provavelmente alguma mágica de referência de tempo de link que evita a duplicação de dados. Se você gravar uma sequência codificada, ela será inserida nos dados apenas uma vez, em vez de duas vezes o que seria esperado se fosse o bytecode normal do LLVM.

Segundo, o código de bits não é realmente enviado no arquivo binário como uma arquitetura separada, como seria de esperar. Ele não é enviado da mesma maneira que digamos x86 e ARM são colocados em um binário (arquivo FAT). Em vez disso, eles usam uma seção especial no binário MachO específico da arquitetura chamado "__LLVM", que é enviado com todas as arquiteturas suportadas (isto é, duplicadas). Eu suponho que essa é uma breve vinda do sistema do compilador e pode ser corrigida no futuro para evitar a duplicação.

Código C (compilado com clang -fembed-bitcode hi.c -S -emit-llvm):

#include <stdio.h>

int main() {
    printf("hi there!");
    return 0;
}

Saída IR LLVM:

; ModuleID = '/var/folders/rd/sv6v2_f50nzbrn4f64gnd4gh0000gq/T/hi-a8c16c.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"

@.str = private unnamed_addr constant [10 x i8] c"hi there!\00", align 1
@llvm.embedded.module = appending constant [1600 x i8] c"\DE\C0\17\0B\00\00\00\00\14\00\00\00$\06\00\00\07\00\00\01BC\C0\DE!\0C\00\00\86\01\00\00\0B\82 \00\02\00\00\00\12\00\00\00\07\81#\91A\C8\04I\06\1029\92\01\84\0C%\05\08\19\1E\04\8Bb\80\10E\02B\92\0BB\84\102\148\08\18I\0A2D$H\0A\90!#\C4R\80\0C\19!r$\07\C8\08\11b\A8\A0\A8@\C6\F0\01\00\00\00Q\18\00\00\C7\00\00\00\1Bp$\F8\FF\FF\FF\FF\01\90\00\0D\08\03\82\1D\CAa\1E\E6\A1\0D\E0A\1E\CAa\1C\D2a\1E\CA\A1\0D\CC\01\1E\DA!\1C\C8\010\87p`\87y(\07\80p\87wh\03s\90\87ph\87rh\03xx\87tp\07z(\07yh\83r`\87th\07\80\1E\E4\A1\1E\CA\01\18\DC\E1\1D\DA\C0\1C\E4!\1C\DA\A1\1C\DA\00\1E\DE!\1D\DC\81\1E\CAA\1E\DA\A0\1C\D8!\1D\DA\A1\0D\DC\E1\1D\DC\A1\0D\D8\A1\1C\C2\C1\1C\00\C2\1D\DE\A1\0D\D2\C1\1D\CCa\1E\DA\C0\1C\E0\A1\0D\DA!\1C\E8\01\1D\00s\08\07v\98\87r\00\08wx\876p\87pp\87yh\03s\80\876h\87p\A0\07t\00\CC!\1C\D8a\1E\CA\01 \E6\81\1E\C2a\1C\D6\A1\0D\E0A\1E\DE\81\1E\CAa\1C\E8\E1\1D\E4\A1\0D\C4\A1\1E\CC\C1\1C\CAA\1E\DA`\1E\D2A\1F\CA\01\C0\03\80\A0\87p\90\87s(\07zh\83q\80\87z\00\C6\E1\1D\E4\A1\1C\E4\00 \E8!\1C\E4\E1\1C\CA\81\1E\DA\C0\1C\CA!\1C\E8\A1\1E\E4\A1\1C\E6\01X\83y\98\87y(\879`\835\18\07|\88\03;`\835\98\87y(\076X\83y\98\87r\90\036X\83y\98\87r\98\03\80\A8\07w\98\87p0\87rh\03s\80\876h\87p\A0\07t\00\CC!\1C\D8a\1E\CA\01 \EAa\1E\CA\A1\0D\E6\E1\1D\CC\81\1E\DA\C0\1C\D8\E1\1D\C2\81\1E\00s\08\07v\98\87r\006\C8\88\F0\FF\FF\FF\FF\03\C1\0E\E50\0F\F3\D0\06\F0 \0F\E50\0E\E90\0F\E5\D0\06\E6\00\0F\ED\10\0E\E4\00\98C8\B0\C3<\94\03@\B8\C3;\B4\819\C8C8\B4C9\B4\01<\BCC:\B8\03=\94\83<\B4A9\B0C:\B4\03@\0F\F2P\0F\E5\00\0C\EE\F0\0Em`\0E\F2\10\0E\EDP\0Em\00\0F\EF\90\0E\EE@\0F\E5 \0FmP\0E\EC\90\0E\ED\D0\06\EE\F0\0E\EE\D0\06\ECP\0E\E1`\0E\00\E1\0E\EF\D0\06\E9\E0\0E\E60\0Fm`\0E\F0\D0\06\ED\10\0E\F4\80\0E\809\84\03;\CCC9\00\84;\BCC\1B\B8C8\B8\C3<\B4\819\C0C\1B\B4C8\D0\03:\00\E6\10\0E\EC0\0F\E5\00\10\F3@\0F\E10\0E\EB\D0\06\F0 \0F\EF@\0F\E50\0E\F4\F0\0E\F2\D0\06\E2P\0F\E6`\0E\E5 \0Fm0\0F\E9\A0\0F\E5\00\E0\01@\D0C8\C8\C39\94\03=\B4\C18\C0C=\00\E3\F0\0E\F2P\0Er\00\10\F4\10\0E\F2p\0E\E5@\0Fm`\0E\E5\10\0E\F4P\0F\F2P\0E\F3\00\AC\C1<\CC\C3<\94\C3\1C\B0\C1\1A\8C\03>\C4\81\1D\B0\C1\1A\CC\C3<\94\03\1B\AC\C1<\CCC9\C8\01\1B\AC\C1<\CCC9\CC\01@\D4\83;\CCC8\98C9\B4\819\C0C\1B\B4C8\D0\03:\00\E6\10\0E\EC0\0F\E5\00\10\F50\0F\E5\D0\06\F3\F0\0E\E6@\0Fm`\0E\EC\F0\0E\E1@\0F\809\84\03;\CCC9\00\00I\18\00\00\02\00\00\00\13\82`B \00\00\00\89 \00\00\0D\00\00\002\22\08\09 d\85\04\13\22\A4\84\04\13\22\E3\84\A1\90\14\12L\88\8C\0B\84\84L\100s\04H*\00\C5\1C\01\18\94`\88\08\AA0F7\10@3\02\00\134|\C0\03;\F8\05;\A0\836\08\07x\80\07v(\876h\87p\18\87w\98\07|\88\038p\838\80\037\80\83\0DeP\0Em\D0\0Ez\F0\0Em\90\0Ev@\07z`\07t\D0\06\E6\80\07p\A0\07q \07x\D0\06\EE\80\07z\10\07v\A0\07s \07z`\07t\D0\06\B3\10\07r\80\07:\0FDH #EB\80\1D\8C\10\18I\00\00@\00\00\C0\10\A7\00\00 \00\00\00\00\00\00\00\868\08\10\00\02\00\00\00\00\00\00\90\05\02\00\00\08\00\00\002\1E\98\0C\19\11L\90\8C\09&G\C6\04C\9A\22(\01\0AM\D0i\10\1D]\96\97C\00\00\00y\18\00\00\1C\00\00\00\1A\03L\90F\02\134A\18\08&PIC Level\13\84a\D80\04\C2\C05\08\82\83c+\03ab\B2j\02\B1+\93\9BK{s\03\B9q\81q\81\01A\19c\0Bs;k\B9\81\81q\81q\A9\99q\99I\D9\10\14\8D\D8\D8\EC\DA\5C\DA\DE\C8\EA\D8\CA\5C\CC\D8\C2\CE\E6\A6\04C\1566\BB6\974\B227\BA)A\01\00y\18\00\002\00\00\003\08\80\1C\C4\E1\1Cf\14\01=\88C8\84\C3\8CB\80\07yx\07s\98q\0C\E6\00\0F\ED\10\0E\F4\80\0E3\0CB\1E\C2\C1\1D\CE\A1\1Cf0\05=\88C8\84\83\1B\CC\03=\C8C=\8C\03=\CCx\8Ctp\07{\08\07yH\87pp\07zp\03vx\87p \87\19\CC\11\0E\EC\90\0E\E10\0Fn0\0F\E3\F0\0E\F0P\0E3\10\C4\1D\DE!\1C\D8!\1D\C2a\1Ef0\89;\BC\83;\D0C9\B4\03<\BC\83<\84\03;\CC\F0\14v`\07{h\077h\87rh\077\80\87p\90\87p`\07v(\07v\F8\05vx\87w\80\87_\08\87q\18\87r\98\87y\98\81,\EE\F0\0E\EE\E0\0E\F5\C0\0E\EC\00q \00\00\05\00\00\00&`<\11\D2L\85\05\10\0C\804\06@\F8\D2\14\01\00\00a \00\00\0B\00\00\00\13\04A,\10\00\00\00\03\00\00\004#\00dC\19\020\18\83\01\003\11\CA@\0C\83\11\C1\00\00#\06\04\00\1CB\12\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", section "__LLVM,__bitcode"
@llvm.cmdline = appending constant [67 x i8] c"-triple\00x86_64-apple-macosx10.10.0\00-emit-llvm\00-disable-llvm-optzns\00", section "__LLVM,__cmdline"

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([10 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.0.53.3)"}

A matriz de dados que está no IR também muda dependendo da otimização e de outras configurações de geração de código do clang. É completamente desconhecido para mim em que formato ou qualquer coisa que esteja.

EDITAR:

Seguindo a dica no Twitter, decidi revisitar e confirmar. Segui esta postagem do blog e usei sua ferramenta extratora de código de bits para obter o binário do Apple Archive fora do executável do MachO. E depois de extrair o Apple Archive com o utilitário xar, obtive isso (convertido em texto com o llvm-dis, é claro)

; ModuleID = '1'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"

@.str = private unnamed_addr constant [10 x i8] c"hi there!\00", align 1

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.1.76)"}

A única diferença notável realmente entre o IR sem código de bits e o IR com código de bits é que os nomes de arquivos foram reduzidos para apenas 1, 2 etc. para cada arquitetura.

Também confirmei que o código de bits incorporado em um binário é gerado após otimizações. Se você compilar com -O3 e extrair o código de bits, será diferente do que se você compilar com -O0.

E apenas para obter crédito extra, também confirmei que a Apple não envia código de bit para dispositivos quando você baixa um aplicativo iOS 9. Eles incluem várias outras seções estranhas que não reconheci como __LINKEDIT, mas não incluem o pacote __LLVM .__ e, portanto, não parecem incluir código de bits no binário final executado em um dispositivo. Curiosamente, a Apple ainda envia binários gordos com código separado de 32 / 64bit para dispositivos iOS 8.


O código de bits da Apple pode ser descompilado? Ou seja, a Apple agora pode ver o nosso código fonte?
malhal 25/06

@malcolmhall se for semelhante ao código LLVM, então apenas meio. O bytecode do LLVM possui informações de tipo e outras dicas que podem tornar a descompilação muito mais fácil e mais útil. No entanto, não sei o que há no código de bits da Apple. Provavelmente é pelo menos um pouco mais útil, mas não se sabe até que ponto será útil. De qualquer forma eu duvido que vai ser tão forte de informações, como por exemplo como .NET IL permite decompilation quase perfeita de código C #
Earlz

Você já viu o formato de arquivo de código de bits do LLVM ? O número mágico é diferente, mas a Apple está sugerindo fortemente que este é o formato de código de bits.
Jeffery Thomas

"que é fornecido com cada arquitetura suportada (ou seja, duplicado)" não é duplicada, porque bitcode é diferente para cada fatia mach-o
AlexDenisov

2
De acordo com twitter.com/mistydemeo/status/644555663373307904 , xar!é o formato de arquivo da Apple.
Riking 17/09/2015

14

Código de bits (iOS, watchOS)

Bitcode é uma representação intermediária de um programa compilado. Os aplicativos enviados para o iTunes Connect que contêm código de bits serão compilados e vinculados na App Store. A inclusão de código de bits permitirá que a Apple otimize novamente o binário do seu aplicativo no futuro, sem a necessidade de enviar uma nova versão do seu aplicativo para a loja.


Basicamente, esse conceito é um pouco semelhante ao java, onde o código de bytes é executado em diferentes JVMs e, nesse caso, o código de bits é colocado na loja iTune e, em vez de fornecer o código intermediário para diferentes plataformas (dispositivos), fornece o código compilado que não precisa qualquer máquina virtual para executar.

Portanto, precisamos criar o código de bits uma vez e ele estará disponível para dispositivos existentes ou futuros. É a dor de cabeça da Apple compilar e torná-lo compatível com cada plataforma que eles têm.

Os desenvolvedores não precisam fazer alterações e enviar o aplicativo novamente para dar suporte a novas plataformas.

Vamos dar o exemplo do iPhone 5s quando a Apple introduziu um x64chip nele. Embora os x86aplicativos sejam totalmente compatíveis com a x64arquitetura, mas para utilizar totalmente a x64plataforma, o desenvolvedor precisa alterar a arquitetura ou algum código. Depois que ele termina, o aplicativo é enviado à loja de aplicativos para revisão.

Se esse conceito de código de bit foi lançado anteriormente, nós, os desenvolvedores, não precisamos fazer alterações para dar suporte à x64arquitetura de bits.


@ user102008 O fatiamento é um resultado de permitir Bitcode
Keji

@kdogisthebest: Não, não é. Em nenhum lugar diz isso. E eu assisti o vídeo da WWDC sobre fatiamento, e nem todas as menções habilitam o Bitcode.
user102008

Inder Kumar Rathore quando se trata da Enterprise App Store Como funciona? a loja de aplicativos corporativos suporta esse recurso?
damithH

@damithH Não há loja de aplicativos corporativos, precisamos manter os aplicativos em nossos servidores. Não tenho certeza se o afinamento de aplicativos funcionará ou não em aplicativos empresariais. Mas, tanto quanto eu saiba que não deveria estar lá para aplicativos corporativos
Inder Kumar Rathore

Atualize a imagem na sua resposta, não está relacionada ao Bitcode.
hsafarya

5

Atualizar

A Apple esclareceu que a fatia ocorre independentemente da ativação do código de bits. Também observei isso na prática, onde um aplicativo sem código de bit será baixado apenas como a arquitetura apropriada para o dispositivo de destino.

Original

Mais especificamente :

Código de bits. Arquive seu aplicativo para envio à App Store em uma representação intermediária, que é compilada em executáveis ​​de 64 ou 32 bits para os dispositivos de destino quando entregues.

Fatiamento. As obras de arte incorporadas ao Catálogo de ativos e marcadas para uma plataforma permitem que a App Store forneça apenas o necessário para a instalação.

Da maneira que eu li isso, se você oferecer suporte a código de bits, os downloaders do seu aplicativo obterão apenas a arquitetura compilada necessária para o seu próprio dispositivo.


No guia App Thinning ( developer.apple.com/library/prerelease/ios/documentation/IDEs/… ) "O fatiamento é o processo de criação e entrega de variantes do pacote de aplicativos para diferentes dispositivos de destino. Uma variante contém apenas a arquitetura executável e recursos necessários para o dispositivo de destino ". Os downloaders do seu aplicativo que obtêm apenas a arquitetura deles fazem parte do Slicing.
user102008
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.