Enciclopedia de prueba

Informar un problema Ver código fuente Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Una especificación exhaustiva del entorno de ejecución de pruebas.

Segundo plano

El lenguaje de compilación de Bazel incluye reglas que se pueden usar para definir programas de prueba automáticos en muchos lenguajes.

Las pruebas se ejecutan con bazel test.

Los usuarios también pueden ejecutar objetos binarios de prueba directamente. Esto está permitido, pero no se recomienda, ya que una invocación de este tipo no cumplirá con los mandatos que se describen a continuación.

Las pruebas deben ser herméticas, es decir, deben acceder solo a los recursos en los que tienen una dependencia declarada. Si las pruebas no son herméticas de forma correcta, no proporcionan resultados reproducibles históricamente. Esto podría ser un problema significativo para encontrar el culpable (determinar qué cambio rompió una prueba), la auditabilidad de la ingeniería de lanzamientos y el aislamiento de recursos de las pruebas (los frameworks de pruebas automatizadas no deben realizar un ataque DDoS a un servidor porque algunas pruebas se comunican con él).

Objetivo

El objetivo de esta página es establecer formalmente el entorno de ejecución y el comportamiento esperado de las pruebas de Bazel. También impondrá requisitos en el ejecutor de pruebas y el sistema de compilación.

La especificación del entorno de prueba ayuda a los autores de pruebas a evitar depender de un comportamiento no especificado y, por lo tanto, le brinda a la infraestructura de pruebas más libertad para realizar cambios de implementación. La especificación cierra algunos agujeros que, en la actualidad, permiten que muchas pruebas se aprueben a pesar de no ser herméticas, deterministas ni reentrantes de forma adecuada.

El objetivo de esta página es ser normativa y autorizada. Si esta especificación y el comportamiento implementado del ejecutor de pruebas no coinciden, la especificación tiene prioridad.

Especificación propuesta

Las palabras clave “DEBER”, “NO DEBER”, “OBLIGATORIO”, “DEBEN”, “NO DEBEN”, “DEBEN”, “NO DEBEN”, “RECOMENDADO”, “PUEDEN” y “OPCIONAL” se deben interpretar como se describe en la RFC 2119 de IETF.

Propósito de las pruebas

El propósito de las pruebas de Bazel es confirmar algunas propiedades de los archivos fuente que se verificaron en el repositorio. (En esta página, "archivos de origen" incluye datos de prueba, resultados de prueba y cualquier otro elemento que se mantenga bajo control de versión). Un usuario escribe una prueba para afirmar una invariante que espera que se mantenga. Otros usuarios ejecutan la prueba más adelante para verificar si se rompió la invariante. Si la prueba depende de variables que no sean archivos de origen (no herméticos), su valor disminuye, ya que los usuarios posteriores no pueden estar seguros de que sus cambios sean los responsables cuando la prueba deja de aprobarse.

Por lo tanto, el resultado de una prueba debe depender solo de lo siguiente:

  • archivos de origen en los que la prueba tiene una dependencia declarada
  • productos del sistema de compilación en los que la prueba tiene una dependencia declarada
  • recursos cuyo comportamiento garantiza el ejecutor de pruebas para que permanezca constante

Actualmente, no se aplica ese comportamiento. Sin embargo, los ejecutores de pruebas se reservan el derecho de agregar esa aplicación forzosa en el futuro.

Rol del sistema de compilación

Las reglas de prueba son análogas a las reglas binarias en que cada una debe generar un programa ejecutable. En algunos lenguajes, este es un programa stub que combina un harness específico del lenguaje con el código de prueba. Las reglas de prueba también deben producir otros resultados. Además del ejecutable de prueba principal, el ejecutor de pruebas necesitará un manifiesto de runfiles, archivos de entrada que deben estar disponibles para la prueba en el tiempo de ejecución, y es posible que necesite información sobre el tipo, el tamaño y las etiquetas de una prueba.

El sistema de compilación puede usar los archivos de ejecución para entregar código y datos. (Esto se puede usar como una optimización para que cada prueba binaria sea más pequeña compartiendo archivos entre pruebas, como a través del uso de la vinculación dinámica). El sistema de compilación debe garantizar que el ejecutable generado cargue estos archivos a través de la imagen de runfiles que proporciona el ejecutor de pruebas, en lugar de referencias codificadas a ubicaciones absolutas en el árbol de origen o de salida.

Rol del ejecutor de pruebas

Desde el punto de vista del ejecutor de pruebas, cada prueba es un programa que se puede invocar con execve(). Puede haber otras formas de ejecutar pruebas. Por ejemplo, un IDE podría permitir la ejecución de pruebas de Java en proceso. Sin embargo, el resultado de ejecutar la prueba como un proceso independiente se debe considerar confiable. Si un proceso de prueba se ejecuta hasta completarse y finaliza de forma normal con un código de salida de cero, la prueba se aprobó. Cualquier otro resultado se considera un error de prueba. En particular, escribir cualquiera de las cadenas PASS o FAIL en stdout no tiene importancia para el ejecutor de pruebas.

Si una prueba tarda demasiado en ejecutarse, supera algún límite de recursos o el ejecutor de pruebas detecta un comportamiento prohibido, es posible que se cancele la prueba y se trate la ejecución como un error. El ejecutor no debe informar que la prueba se aprobó después de enviar una señal al proceso de prueba o a cualquiera de sus elementos secundarios.

Se le otorga un tiempo limitado a todo el objetivo de prueba (no a métodos o pruebas individuales) para que se ejecute hasta completarse. El límite de tiempo de una prueba se basa en su atributo timeout según la siguiente tabla:

tiempo de espera agotado Límite de tiempo (seg.)
short 60
Moderada 300
long 900
eterno 3600

Las pruebas que no especifican explícitamente un tiempo de espera tienen uno implícito según el size de la prueba de la siguiente manera:

tamaño Etiqueta de tiempo de espera implícito
poco a poco short
media Moderada
grandes long
enorme eterno

A una prueba "grande" sin configuración de tiempo de espera explícito se le asignarán 900 segundos para ejecutarse. Una prueba "media" con un tiempo de espera "corto" tendrá 60 segundos.

A diferencia de timeout, size también determina el uso máximo supuesto de otros recursos (como la RAM) cuando se ejecuta la prueba de forma local, como se describe en Definiciones comunes.

Todas las combinaciones de etiquetas size y timeout son legales, por lo que se puede declarar que una prueba "enorme" tiene un tiempo de espera "corto". Presumiblemente, haría cosas muy horribles con rapidez.

Las pruebas pueden mostrar resultados arbitrariamente rápidos, independientemente del tiempo de espera. Una prueba no se penaliza por un tiempo de espera demasiado generoso, aunque se puede emitir una advertencia. Por lo general, debes establecer el tiempo de espera lo más ajustado posible sin generar inconsistencias.

El tiempo de espera de la prueba se puede anular con la marca --test_timeout de Bazel cuando se ejecuta de forma manual en condiciones que se sabe que son lentas. Los valores de --test_timeout se expresan en segundos. Por ejemplo, --test_timeout=120 establece el tiempo de espera de la prueba en dos minutos.

También hay un límite inferior recomendado para los tiempos de espera de las pruebas, como se indica a continuación:

tiempo de espera agotado Tiempo mínimo (s)
short 0
Moderada 30
long 300
eterno 900

Por ejemplo, si una prueba "moderada" se completa en 5.5 s, considera establecer timeout = "short" o size = "small". Si usas la opción de línea de comandos --test_verbose_timeout_warnings de Bazel, se mostrarán las pruebas cuyo tamaño especificado es demasiado grande.

Los tiempos de espera y los tamaños de prueba se especifican en el archivo BUILD según las especificaciones que se indican aquí. Si no se especifica, el tamaño de una prueba será “mediano” de forma predeterminada.

Si el proceso principal de una prueba finaliza, pero algunos de sus procesos secundarios aún se están ejecutando, el ejecutor de pruebas debe considerar que la ejecución se completó y registrarla como un éxito o una falla según el código de salida observado desde el proceso principal. El ejecutor de pruebas puede finalizar cualquier proceso erróneo. Las pruebas no deben filtrar procesos de esta manera.

Particionamiento de pruebas

Las pruebas se pueden realizar en paralelo mediante la fragmentación de pruebas. Consulta --test_sharding_strategy y shard_count para habilitar la fragmentación de prueba. Cuando se habilita el fragmentación, el ejecutor de pruebas se inicia una vez por fragmento. La variable de entorno TEST_TOTAL_SHARDS es la cantidad de fragmentos, y TEST_SHARD_INDEX es el índice de fragmentos, que comienza en 0. Los ejecutores usan esta información para seleccionar qué pruebas ejecutar, por ejemplo, con una estrategia de asignación de tareas en el orden en que llegan. No todos los ejecutores de pruebas admiten el fragmentación. Si un ejecutor admite el fragmentación, debe crear o actualizar la fecha de la última modificación del archivo especificada por TEST_SHARD_STATUS_FILE. De lo contrario, si está habilitada --incompatible_check_sharding_support, Bazel fallará en la prueba si está fragmentada.

Condiciones iniciales

Cuando se ejecuta una prueba, el ejecutor de pruebas debe establecer ciertas condiciones iniciales.

El ejecutor de pruebas debe invocar cada prueba con la ruta de acceso al ejecutable de prueba en argv[0]. Esta ruta de acceso debe ser relativa y estar debajo del directorio actual de la prueba (que se encuentra en el árbol de runfiles, consulta a continuación). El ejecutor de pruebas no debe pasar ningún otro argumento a una prueba, a menos que el usuario lo solicite de forma explícita.

El bloque de entorno inicial se debe componer de la siguiente manera:

Variable Valor Estado
HOME valor de $TEST_TMPDIR recomendado
LANG unset obligatorio
LANGUAGE unset obligatorio
LC_ALL unset obligatorio
LC_COLLATE unset obligatorio
LC_CTYPE unset obligatorio
LC_MESSAGES unset obligatorio
LC_MONETARY unset obligatorio
LC_NUMERIC unset obligatorio
LC_TIME unset obligatorio
LD_LIBRARY_PATH Es una lista de directorios separados por dos puntos que contienen bibliotecas compartidas. opcional
JAVA_RUNFILES valor de $TEST_SRCDIR obsoleto
LOGNAME valor de $USER obligatorio
PATH /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:. recomendado
PWD $TEST_SRCDIR/workspace-name recomendado
SHLVL 2 recomendado
TEST_INFRASTRUCTURE_FAILURE_FILE Es la ruta de acceso absoluta a un archivo privado en un directorio en el que se puede escribir (este archivo solo debe usarse para informar fallas que se originan en la infraestructura de pruebas, no como un mecanismo general para informar fallas inestables de las pruebas). En este contexto, la infraestructura de pruebas se define como sistemas o bibliotecas que no son específicos de la prueba, pero que pueden causar fallas en la prueba debido a un funcionamiento incorrecto. La primera línea es el nombre del componente de la infraestructura de pruebas que causó la falla, y la segunda es una descripción legible por humanos de la falla. Se ignoran las líneas adicionales). opcional
TEST_LOGSPLITTER_OUTPUT_FILE Es la ruta de acceso absoluta a un archivo privado en un directorio en el que se puede escribir (se usa para escribir el registro de protobuffer de Logsplitter). opcional
TEST_PREMATURE_EXIT_FILE Es la ruta de acceso absoluta a un archivo privado en un directorio en el que se puede escribir (se usa para capturar llamadas a exit()). opcional
TEST_RANDOM_SEED Si se usa la opción --runs_per_test, TEST_RANDOM_SEED se establece en run number (comienza con 1) para cada ejecución de prueba individual. opcional
TEST_RUN_NUMBER Si se usa la opción --runs_per_test, TEST_RUN_NUMBER se establece en run number (comienza con 1) para cada ejecución de prueba individual. opcional
TEST_TARGET Es el nombre del objetivo que se está probando. opcional
TEST_SIZE La prueba size opcional
TEST_TIMEOUT La prueba timeout en segundos opcional
TEST_SHARD_INDEX índice de fragmento, si se usa sharding opcional
TEST_SHARD_STATUS_FILE Es la ruta de acceso al archivo que se debe tocar para indicar la compatibilidad con sharding. opcional
TEST_SRCDIR Es la ruta de acceso absoluta a la base del árbol de runfiles. obligatorio
TEST_TOTAL_SHARDS total shard count, si se usa sharding opcional
TEST_TMPDIR ruta de acceso absoluta a un directorio privado en el que se puede escribir obligatorio
TEST_WORKSPACE el nombre del lugar de trabajo del repositorio local opcional
TEST_UNDECLARED_OUTPUTS_DIR Es la ruta de acceso absoluta a un directorio privado en el que se puede escribir (se usa para escribir resultados de pruebas no declarados). opcional
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR Es la ruta de acceso absoluta a un directorio privado en el que se puede escribir (se usa para escribir archivos .part y .pb de anotaciones de resultados de prueba no declarados). opcional
TEST_WARNINGS_OUTPUT_FILE Es la ruta de acceso absoluta a un archivo privado en un directorio en el que se puede escribir (se usa para escribir advertencias de destino de prueba). opcional
TESTBRIDGE_TEST_ONLY valor de --test_filter, si se especifica opcional
TZ UTC obligatorio
USER valor de getpwuid(getuid())->pw_name obligatorio
XML_OUTPUT_FILE Es la ubicación en la que las acciones de prueba deben escribir un archivo de salida XML de resultados de prueba. De lo contrario, Bazel genera un archivo de salida XML predeterminado que une el registro de prueba como parte de la acción de prueba. El esquema XML se basa en el esquema de resultados de pruebas de JUnit. opcional
BAZEL_TEST Indica que bazel test controla el ejecutable de prueba. obligatorio

El entorno puede contener entradas adicionales. Las pruebas no deben depender de la presencia, ausencia o valor de ninguna variable de entorno que no se haya mencionado anteriormente.

El directorio de trabajo inicial debe ser $TEST_SRCDIR/$TEST_WORKSPACE.

No se especifican el ID de proceso actual, el ID de grupo de procesos, el ID de sesión ni el ID de proceso superior. El proceso puede ser o no un líder de grupo de procesos o un líder de sesión. El proceso puede tener o no una terminal de control. El proceso puede tener cero o más procesos secundarios en ejecución o sin cosechar. El proceso no debe tener varios subprocesos cuando el código de prueba toma el control.

El descriptor de archivo 0 (stdin) debe estar abierto para la lectura, pero no se especifica a qué está adjunto. Las pruebas no deben leer desde él. Los descriptores de archivos 1 (stdout) y 2 (stderr) deben estar abiertos para la escritura, pero no se especifica a qué están adjuntos. Puede ser una terminal, un tubo, un archivo normal o cualquier otra cosa en la que se puedan escribir caracteres. Pueden compartir una entrada en la tabla de archivos abiertos (lo que significa que no pueden buscar de forma independiente). Las pruebas no deben heredar ningún otro descriptor de archivo abierto.

La umask inicial debe ser 022 o 027.

No debe haber alarmas ni temporizadores de intervalo pendientes.

La máscara inicial de los indicadores bloqueados debe estar vacía. Todos los indicadores se deben configurar en su acción predeterminada.

Los límites de recursos iniciales, tanto los flexibles como los estrictos, deben establecerse de la siguiente manera:

Recurso Límite
RLIMIT_AS unlimited
RLIMIT_CORE sin especificar
RLIMIT_CPU unlimited
RLIMIT_DATA unlimited
RLIMIT_FSIZE unlimited
RLIMIT_LOCKS unlimited
RLIMIT_MEMLOCK unlimited
RLIMIT_MSGQUEUE sin especificar
RLIMIT_NICE sin especificar
RLIMIT_NOFILE al menos 1024
RLIMIT_NPROC sin especificar
RLIMIT_RSS unlimited
RLIMIT_RTPRIO sin especificar
RLIMIT_SIGPENDING sin especificar
RLIMIT_STACK ilimitado o 2044 KB <= rlim <= 8192 KB

No se especifican los tiempos de proceso iniciales (como los muestra times()) ni el uso de recursos (como los muestra getrusage()).

No se especifican la política de programación inicial ni la prioridad.

Rol del sistema host

Además de los aspectos del contexto del usuario bajo el control directo del ejecutor de pruebas, el sistema operativo en el que se ejecutan las pruebas debe satisfacer ciertas propiedades para que una ejecución de prueba sea válida.

Sistema de archivos

El directorio raíz que observa una prueba puede ser o no el directorio raíz real.

Se debe activar /proc.

Todas las herramientas de compilación deben estar presentes en las rutas de acceso absolutas de /usr que usa una instalación local.

Es posible que las rutas que comienzan con /home no estén disponibles. Las pruebas no deben acceder a ninguna ruta de este tipo.

/tmp debe ser de escritura, pero las pruebas deben evitar usar estas rutas de acceso.

Las pruebas no deben suponer que hay una ruta de acceso constante disponible para su uso exclusivo.

Las pruebas no deben suponer que los tiempos de acceso están habilitados para ningún sistema de archivos activado.

Usuarios y grupos

Los usuarios root, nobody y unittest deben existir. Los grupos root, nobody y eng deben existir.

Las pruebas se deben ejecutar como un usuario no raíz. Los IDs de usuario reales y efectivos deben ser iguales, al igual que los IDs de grupo. Además, no se especifican el ID de usuario, el ID de grupo, el nombre de usuario ni el nombre del grupo actuales. No se especifica el conjunto de IDs de grupo complementarios.

El ID de usuario y el ID de grupo actuales deben tener los nombres correspondientes, que se pueden recuperar con getpwuid() y getgrgid(). Es posible que no ocurra lo mismo con los IDs de grupo complementarios.

El usuario actual debe tener un directorio principal. Es posible que no se pueda escribir. Las pruebas no deben intentar escribir en él.

Redes

No se especifica el nombre de host. Puede contener o no un punto. La resolución del nombre de host debe proporcionar una dirección IP del host actual. La resolución del nombre de host cortado después del primer punto también debe funcionar. Se debe resolver el nombre de host localhost.

Otros recursos

Las pruebas se otorgan con, al menos, un núcleo de CPU. Es posible que haya otros disponibles, pero no se garantiza. No se especifican otros aspectos de rendimiento de este núcleo. Puedes aumentar la reserva a una mayor cantidad de núcleos de CPU si agregas la etiqueta "cpu:n" (donde n es un número positivo) a una regla de prueba. Si una máquina tiene menos núcleos de CPU totales que los solicitados, Bazel ejecutará la prueba de todos modos. Si una prueba usa fragmentación, cada fragmento individual reservará la cantidad de núcleos de CPU especificada aquí.

Las pruebas pueden crear subprocesos, pero no grupos ni sesiones de procesamiento.

Existe un límite para la cantidad de archivos de entrada que puede consumir una prueba. Este límite está sujeto a cambios, pero actualmente está en el rango de decenas de miles de entradas.

Hora y fecha

No se especifican la hora ni la fecha actuales. La zona horaria del sistema no está especificada.

Es posible que X Windows esté o no disponible. Las pruebas que necesitan un servidor X deben iniciar Xvfb.

Prueba la interacción con el sistema de archivos

Todas las rutas de acceso a archivos especificadas en las variables de entorno de prueba apuntan a un lugar del sistema de archivos local, a menos que se especifique lo contrario.

Las pruebas deben crear archivos solo dentro de los directorios especificados por $TEST_TMPDIR y $TEST_UNDECLARED_OUTPUTS_DIR (si están configurados).

Estos directorios estarán inicialmente vacíos.

Las pruebas no deben intentar quitar, cambiar el modo de acceso ni alterar de ninguna otra manera estos directorios.

Es posible que estos directorios sean vínculos simbólicos.

El tipo de sistema de archivos de $TEST_TMPDIR/. no se especifica.

Las pruebas también pueden escribir archivos .part en $TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR para anotar archivos de salida no declarados.

En casos excepcionales, es posible que una prueba se vea obligada a crear archivos en /tmp. Por ejemplo, los límites de longitud de ruta para los sockets de dominio Unix suelen requerir la creación del socket en /tmp. Bazel no podrá hacer un seguimiento de esos archivos. La prueba en sí debe tener cuidado de ser hermética, usar rutas de acceso únicas para evitar colisiones con otras pruebas y procesos que se ejecutan de forma simultánea, y limpiar los archivos que crea en /tmp.

Algunos frameworks de pruebas populares, como JUnit4 TemporaryFolder o Go TempDir, tienen sus propias formas de crear un directorio temporal en /tmp. Estos frameworks de pruebas incluyen funcionalidad que limpia los archivos en /tmp, por lo que puedes usarlos, a pesar de que crean archivos fuera de TEST_TMPDIR.

Las pruebas deben acceder a las entradas a través del mecanismo de runfiles o de otras partes del entorno de ejecución que están diseñadas específicamente para que los archivos de entrada estén disponibles.

Las pruebas no deben acceder a otros resultados del sistema de compilación en rutas inferidas de la ubicación de su propio ejecutable.

No se especifica si el árbol de runfiles contiene archivos normales, vínculos simbólicos o una combinación. El árbol de runfiles puede contener symlinks a directorios. Las pruebas deben evitar usar rutas de acceso que contengan componentes .. dentro del árbol de archivos de ejecución.

No se debe poder escribir en ningún directorio, archivo ni symlink dentro del árbol de archivos de ejecución (incluidas las rutas que atraviesan symlinks). (Se deduce que el directorio de trabajo inicial no debe ser de escritura). Las pruebas no deben dar por sentado que alguna parte de los archivos de ejecución es de escritura o que es propiedad del usuario actual (por ejemplo, es posible que chmod y chgrp fallen).

El árbol de runfiles (incluidas las rutas de acceso que atraviesan symlinks) no debe cambiar durante la ejecución de la prueba. Los directorios superiores y las activaciones del sistema de archivos no deben cambiar de ninguna manera que afecte el resultado de la resolución de una ruta de acceso dentro del árbol de runfiles.

Para detectar la salida anticipada, una prueba puede crear un archivo en la ruta de acceso especificada por TEST_PREMATURE_EXIT_FILE al inicio y quitarlo al salir. Si Bazel ve el archivo cuando finalice la prueba, supondrá que la prueba se cerró antes de tiempo y la marcará como fallida.

Convenciones de etiquetas

Algunas etiquetas de las reglas de prueba tienen un significado especial. Consulta también la Enciclopedia de compilación de Bazel sobre el atributo tags.

Etiqueta Significado
exclusive no ejecutar ninguna otra prueba al mismo tiempo
external La prueba tiene una dependencia externa. Inhabilita la caché de prueba.
large Convención test_suite; conjunto de pruebas grandes
manual * No incluyas el objetivo de prueba en patrones de destino de comodín, como :..., :* o :all.
medium Convención test_suite; paquete de pruebas medianas
small Convención test_suite; conjunto de pruebas pequeñas
smoke Convención de test_suite; significa que se debe ejecutar antes de confirmar los cambios de código en el sistema de control de versiones.

Archivos de ejecución

A continuación, supongamos que hay una regla *_binary() etiquetada como //foo/bar:unittest, con una dependencia de tiempo de ejecución en la regla etiquetada como //deps/server:server.

Ubicación

El directorio de runfiles para un //foo/bar:unittest de destino es el directorio $(WORKSPACE)/$(BINDIR)/foo/bar/unittest.runfiles. Esta ruta se conoce como runfiles_dir.

Dependencias

El directorio runfiles se declara como una dependencia del tiempo de compilación de la regla *_binary(). El directorio runfiles depende del conjunto de archivos BUILD que afectan la regla *_binary() o cualquiera de sus dependencias de tiempo de compilación o de tiempo de ejecución. La modificación de los archivos de origen no afecta la estructura del directorio de archivos de ejecución y, por lo tanto, no activa ninguna compilación.

Contenido

El directorio runfiles contiene lo siguiente:

  • Vínculos simbólicos a dependencias del tiempo de ejecución: Cada OutputFile y CommandRule que es una dependencia del tiempo de ejecución de la regla *_binary() está representada por un vínculo simbólico en el directorio de archivos de ejecución. El nombre del symlink es $(WORKSPACE)/package_name/rule_name. Por ejemplo, el symlink del servidor se llamaría $(WORKSPACE)/deps/server/server y la ruta completa sería $(WORKSPACE)/foo/bar/unittest.runfiles/$(WORKSPACE)/deps/server/server. El destino del symlink es OutputFileName() de OutputFile o CommandRule, expresado como una ruta de acceso absoluta. Por lo tanto, el destino del symlink podría ser $(WORKSPACE)/linux-dbg/deps/server/42/server.
  • Vínculos simbólicos a subarchivos de ejecución: Para cada *_binary() Z que es una dependencia del tiempo de ejecución de *_binary() C, hay un segundo vínculo en el directorio de archivos de ejecución de C a los archivos de ejecución de Z. El nombre del symlink es $(WORKSPACE)/package_name/rule_name.runfiles. El destino del symlink es el directorio runfiles. Por ejemplo, todos los subprogramas comparten un directorio de archivos de ejecución común.