Skip to main content
Version: 🚧 Alpha 🚧

Export Plugin : Technical Documentation

Available features​

The list of available features contains:

  • Exporting as a zip file
  • Exporting as a single file native executable using a self extracted archive (except for MacOS)
  • Include the user’s JDK to the export (optional)
  • Export one or more experiments
  • Export one or more models
  • Accessed data files through the file("../includes/building.shp") syntax are automatically included in the export if located outside of the GAMA project of the target models
  • GAMA linked files are handled
  • The exported application has an embedded workspace
  • If exported, user projects are copied in the embedded workspace
  • If exported, the projects from the model library are copied in the same path relative to the exported app
  • The exported app doesn’t update the workspace metadata
  • The exported app contains the minimal set of needed gama plugin for the exported plugin
  • Many interface elements are disabled in the simulation perspective of the exported application, as well as shortcuts.
  • Preferences configuration menus are completely removed in the exported application
  • In the exported app, simulation artifacts can be saved in the directory of choice

Launching a simulation​

The entry point of the GAMA platform reads the flag SIMULATION_ONLY. If it is set to true, the class StartupModelHelper is instantiated.

The flags CORE_DEFAULT_MODEL and CORE_DEFAULT_EXPERIMENT are read to retrieve the default model and experiments stored in the preferences. If multiple experiments are detected, the instantiation triggers a selection menu by calling the method pickExperiment.

If experiments are from multiple models, the CORE_DEFAULT_MODEL flag is the empty string, and the string value of CORE_DEFAULT_EXPERIMENT has the following form :

first_experiment@/projectA/models/first_model.gaml#second_experiment@/projectB/models/second_model.gaml

These flags are defined in the preference file located in /configuration/.settings/gama.prefs, within the GAMA installation directory.

ExportActivator resolves the path of the active eclipse distribution, and resolves the project in the directory Embedded_Workspace/, by searching for .project files, and opens them to make sure they are registered in eclipse's resource system.

StartupModelHelper handles:

  • retrieval of the available models and experiments through the flags set by the preferences.
  • experiment selection/switching
  • model compiling
  • simulation launching

The same instance of StartupModelHelper is accessed throughout the program through a getInstance static method, allowing the class to hold data related to the available models and experiments.

Building an archive​

User interfaces handling export configuration are calling the class GamaZipBuilder to create the exported application. GamaZipBuilder is the entry point of the export logic. Each export must be bound to a new instance. Every configuration option of the export is an argument passed to its constructor. The configuration happens in the class ExportProjectAsSimulation located in the plugin gama.ui.navigator.

GamaZipBuilder is :

  1. resolving the minimal set of needed plugins from the subset of plugins passed as constructor arguments (determined by the dynamic analysis), through static manifest based dependency analysis.

  2. Embedding GAMA’s app files in an archive file, and filtering useless plugins, and directories that are generated on startup (for example configuration/org.eclipse.osgi/ is not included)

  3. The file Gama.ini is edited to set the flags use_global_preference_store and simulation_only to true, and the argument persistState is set to false. If the zipWithJdk field is true, the correct “-vm ./jdk/bin/java” variant (depending on the target OS) is appended to the Gama.ini file.

  4. the following preferences are edited in the export : workspace_path, workspace_remember, startup_model, default_experiment, errors_in_editor, ask_rebuild and ask_outdated

  5. External data files dependencies are resolved

  6. The target project, and the projects containing models which are imported by the target models, are copied in the export’s embedded workspace, with, if needed, the external data files copied inside and their references updated.

  7. Resources referring to eclipse links instead of actual files are resolved and copied in the expected path in the export, by reading the .project metadata file of each included project.

  8. The .project metadata file is edited to remove the links.

  9. If the zipWithJdk field is true, the active jdk is resolved and copied in the export.

  10. If on MacOS, a troubleshoot.txt file containing instructions for disabling the gatekeeper is appended at the root of the archive. It’s a clever way of forcing MacOS to extract the content in a new folder having the same name as the archive, and which is recognized as an application.

  11. If the isOneFileExport field is true, a native executable working as a self extracting archive is created :
    a. If the OS is Linux, the archive is a zip file, and is concatenated to the files gama.plugin/export/binaries/runner and gama.plugin/export/binaries/zipextractor in such a way :
    beginning of the file → binaries/runner → binaries/zipextractor → archive.zip → end of the file
    Then the binaries/runner file is patched to update elf64 headers and custom metadata.

    b. If the OS is Windows, the archive is a 7z file and is appended to the file gama.plugin/export/binaries/7zS2.sfx is such a way :
    beginning of the file → binaries/7zS2.sfx → archive.zip → end of the file

The class BundleDependencyAnalyzer is in charge of the static manifest based dependency analysis, from the jar plugins.

The class ArchiveBuilder exposes high abstraction methods to manipulate zip or 7z archives seamlessly. This is important because if the isOneFileExport field is true, the archive must be a zip on Linux, and a 7z archive on Windows.

Building a single file executable​

On Windows and Linux, a self extracting archive is built out of the archive made by GamaZipBuilder. It consists of a program to which is appended the archive, and which extracts the archive in a temporary directory before launching the entrypoint by itself.

On Windows, the solution from 7z is used, where appending a 7z archive to the 7zS2.sfx runner is enough, to extract the archive and automatically find the executable
https://7-zip.opensource.jp/chm/cmdline/switches/sfx.html

On Linux, the solution works the same, but is custom. The executable produced contains 3 components :

  • binaries/runner, the entrypoint of the program. It's written in assembly x64, assembled with nasm. Assembly allows the program to be smaller and reduces the amount of patching required after the concatenation with other components.
  • binaries/zipextractor, a small program for extracting the content of a zip archive, written in golang. Its only purpose is to be called by binaries/runner, because writing the unzip function in assembly would be unnecessarily difficult, and using libraries would complexify the layout of the executable file and memory.
    usage: zipextractor [-v] <source_file> <destination_dir>

The class ElfPacker is in charge of patching the native executable produced for Linux, by updating the elf64 headers to correct the new total size of the file, and fill a custom metadata table located at the end of gama.plugin/export/binaries/runner and containing 5 strings of 1024 bytes and 3 integers of 8 bytes.

These are appended to the file in the following order :

  • String 1 : path to the temporary directory where the zip archive will be extracted
  • String 2: path where zipextractor will be written on the file system
  • String 3: path where the archive payload will be written on the file system
  • String 4 : path where the content of the archive payload will be extracted
  • String 5 : path to the entrypoint of GAMA, in the tmp directory
  • Int 1 : zipextractor size
  • Int 2 : offset to archive in memory (also the zip extractor size)
  • Int 3 : size of the archive

binaries/runner :

  1. Creates the directory pointed by the path in String 1
  2. Writes the zipextractor on the file system at the path pointed by String 2
  3. Writes the archive on the file system at the path pointed by String 3
  4. calls the zipextractor with the correct argument to unzip the archive in the path pointed by String 4
  5. zipextractor does the actual archive extraction
  6. calls the entrypoint of GAMA pointed by String 5