Dependencias

Informar un problema . Ver fuente . Por la noche · 7.2 · 7.1 · 7.0 · 6.5 · 6.4

Un A de destino depende de un B de destino si A necesita B en la compilación o tiempo de ejecución. La relación depende de induce una Grafo acíclico dirigido (DAG) sobre los objetivos y se denomina gráfico de dependencia.

Las dependencias directas de un objetivo son aquellos otros objetivos a los que puede llegar una ruta de longitud 1 en el gráfico de dependencia. Las dependencias transitivas de un destino se los objetivos de los que depende mediante una ruta de cualquier longitud a través del gráfico.

De hecho, en el contexto de compilaciones, hay dos gráficos de dependencias: el grafo de las dependencias reales y el gráfico de las dependencias declaradas. La mayoría de las tiempo, los dos gráficos son tan similares que no hace falta hacer esta distinción, pero es útil para la siguiente discusión.

Dependencias reales y declaradas

Un X de destino en realidad depende del Y de destino si debe estar presente Y compilada y actualizada para que X se compile correctamente. Compilada podría significa generar, procesar, compilar, vincular, archivar, comprimir, ejecutar o cualquiera de los otros tipos de tareas que ocurren habitualmente durante una compilación.

Un X de destino tiene una dependencia declarada en el Y de destino si hay una dependencia borde de X a Y en el paquete de X.

Para compilaciones correctas, el gráfico de dependencias reales A debe ser un subgrafo de el gráfico de dependencias declaradas D. Es decir, cada par de los nodos conectados directamente x --> y en A también deben conectarse directamente D Se puede decir que D es una sobreaproximación de A.

Los escritores de archivos BUILD deben declarar de forma explícita todos los dependencias para cada regla del sistema de compilación y nada más.

No seguir este principio causa un comportamiento indefinido: la compilación puede fallar, pero lo que es peor, la compilación puede depender de algunas operaciones previas o de declaradas que tiene el objetivo. Bazel comprueba si faltan las dependencias e informar errores, pero no es posible que esta verificación completar en todos los casos.

No debe (ni debe) intentar enumerar todo lo que se importa indirectamente incluso si A lo necesita en el momento de la ejecución.

Durante una compilación de X de destino, la herramienta de compilación inspecciona toda la información transitiva cierre de las dependencias de X para garantizar que se realicen cambios en esas metas se refleja en el resultado final y vuelve a compilar intermedios según sea necesario.

La naturaleza transitiva de las dependencias conduce a un error común. A veces, código en un archivo puede usar el código proporcionado por una dependencia indirecta, una transitiva pero no perimetral directa en el gráfico de la dependencia declarada. Indirecta no aparecen en el archivo BUILD. Debido a que la regla no dependen directamente del proveedor, no hay forma de hacer un seguimiento de los cambios, como se muestra en el siguiente cronograma de ejemplo:

1. Las dependencias declaradas coinciden con las dependencias reales.

Al principio, todo funciona. El código del paquete a usa el código del paquete b. El código del paquete b usa el código del paquete c y, por lo tanto, a de forma transitiva. depende de c.

a/BUILD b/BUILD
rule(
    name = "a",
    srcs = "a.in",
    deps = "//b:b",
)
      
rule(
    name = "b",
    srcs = "b.in",
    deps = "//c:c",
)
      
a / a.in b / b.in
import b;
b.foo();
    
import c;
function foo() {
  c.bar();
}
      
Gráfico de la dependencia declarada con flechas que conectan a, b y c
Gráfico de la dependencia declarada
Gráfico de la dependencia real que coincide con la dependencia declarada
                  gráfico con flechas que conectan a, b y c
Gráfico de dependencia real

Las dependencias declaradas superan la estimación de las dependencias reales. Todo está bien.

2. Agrega una dependencia no declarada

Se genera un peligro latente cuando alguien agrega un código a a que crea una dependencia directa real en c, pero olvida declararla en el archivo de compilación a/BUILD

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
Gráfico de la dependencia declarada con flechas que conectan a, b y c
Gráfico de la dependencia declarada
Gráfico de dependencia real con flechas que conectan a, b y c. Los
                  ahora también conecta A a C. Esto no coincide con el
                  gráfico de dependencia declarada
Gráfico de dependencia real

Las dependencias declaradas ya no sobreestiman las dependencias reales. Esto puede crearse bien, ya que los cierres transitivos de los dos gráficos son iguales pero enmascara un problema: a tiene una dependencia real, pero no declarada, de c.

3. Divergencia entre los gráficos de dependencias declarados y reales

El peligro se revela cuando alguien refactoriza b para que ya no dependa de c, dejando atrás a por error de forma involuntaria. es culpa de ellos mismos.

  b/BUILD
 
rule(
    name = "b",
    srcs = "b.in",
    deps = "//d:d",
)
      
  b / b.in
 
      import d;
      function foo() {
        d.baz();
      }
      
Gráfico de la dependencia declarada con flechas que conectan a y b.
                  b ya no se conecta con c, lo que interrumpe la conexión de a a c
Gráfico de la dependencia declarada
Gráfico de dependencia real en el que se muestra una conexión con b y c
                  pero b ya no se conecta con c
Gráfico de dependencia real

El gráfico de la dependencia declarada ahora es una subaproximación del valor real las dependencias, incluso cuando se cierran transitivamente; es probable que falle la compilación.

El problema podría haberse evitado garantizando que la dependencia real de a a c que se introdujo en el paso 2 se declaró correctamente en el archivo BUILD.

Tipos de dependencias

La mayoría de las reglas de compilación tienen tres atributos para especificar distintos tipos de dependencias genéricas: srcs, deps y data. Estos se explican a continuación. Para más detalles, consulta Atributos comunes a todas las reglas.

Muchas reglas también tienen atributos adicionales para tipos de reglas específicos dependencias, por ejemplo, compiler o resources. Estos se detallan en el Build Encyclopedia

srcs de dependencias

Archivos consumidos directamente por la regla o reglas que generan archivos de origen.

deps de dependencias

Una regla que apunta a módulos compilados por separado y que proporciona archivos de encabezado. símbolos, bibliotecas, datos, etc.

data de dependencias

Es posible que un destino de compilación necesite algunos archivos de datos para ejecutarse correctamente. Estos archivos de datos no son código fuente: no afectan la forma en que se compila el destino. Por ejemplo, un la prueba de unidades podría comparar el resultado de una función con el contenido de un archivo. Cuando compila la prueba de unidades. No necesitas el archivo, pero sí lo necesitas al ejecutar la prueba. Lo mismo se aplica a las herramientas que se inician durante la ejecución.

El sistema de compilación ejecuta pruebas en un directorio aislado donde solo los archivos indicados como Hay data disponibles. Por lo tanto, si un objeto binario, biblioteca o prueba necesita algunos archivos para ejecutarse, especificarlos (o una regla de compilación que los contenga) en data. Por ejemplo:

# I need a config file from a directory named env:
java_binary(
    name = "setenv",
    ...
    data = [":env/default_env.txt"],
)

# I need test data from another directory
sh_test(
    name = "regtest",
    srcs = ["regtest.sh"],
    data = [
        "//data:file1.txt",
        "//data:file2.txt",
        ...
    ],
)

Estos archivos están disponibles mediante la ruta de acceso relativa path/to/data/file. En las pruebas, puedes hacer referencia a estos archivos uniendo las rutas de acceso del código fuente y la ruta de acceso relativa del espacio de trabajo, por ejemplo, ${TEST_SRCDIR}/workspace/path/to/data/file

Usa etiquetas para hacer referencia a directorios

Al revisar nuestros archivos BUILD, quizás notes que algunas etiquetas data se refieren a los directorios. Estas etiquetas terminan con /. o /, como estos ejemplos, que no deberías usar:

No recomendado: data = ["//data/regression:unittest/."]

No recomendado: data = ["testdata/."]

No recomendado: data = ["testdata/"]

Esto parece conveniente, en especial para pruebas, ya que permite que una prueba usar todos los archivos de datos del directorio.

Pero no lo hagas. Para garantizar que las recompilaciones incrementales correctas (y o volver a ejecutar pruebas) después de un cambio, el sistema de compilación debe tener en cuenta el conjunto completo de archivos que son entradas a la compilación (o prueba). Cuando especificas un directorio, el sistema de compilación vuelve a compilar solo cuando el directorio en sí cambios (debido a la adición o eliminación de archivos), pero no podrá detectar en archivos individuales, ya que esos cambios no afectan el directorio contenedor. En lugar de especificar directorios como entradas para el sistema de compilación, debes enumerar el conjunto de archivos que contienen, ya sea explícitamente o con el función glob(). (Usa ** para forzar la glob() para que sean recursivos).

Recomendado: data = glob(["testdata/**"])

Lamentablemente, hay algunas situaciones en las que se deben usar las etiquetas de directorio. Por ejemplo, si el directorio testdata contiene archivos cuyos nombres no cumplan con la sintaxis de las etiquetas a continuación, una enumeración explícita de archivos o el uso de La función glob() produce etiquetas no válidas . Debes usar etiquetas de directorio en este caso, pero ten cuidado con el riesgo asociado de reconstrucciones incorrectas descrita anteriormente.

Si debes usar etiquetas de directorio, recuerda que no puedes consultar el paquete superior con una ruta de acceso ../ relativa; sino una ruta de acceso absoluta como //data/regression:unittest/.

Toda regla externa, como una prueba, que necesite usar varios archivos declarar explícitamente su dependencia en todos ellos. Puedes usar filegroup() para lo siguiente: Agrupa archivos en el archivo BUILD:

filegroup(
        name = 'my_data',
        srcs = glob(['my_unittest_data/*'])
)

Luego, puedes hacer referencia a la etiqueta my_data como la dependencia de datos en tu prueba.

Archivos de COMPILACIÓN Visibilidad