{"id":44947553,"url":"https://github.com/globulus/mmap","last_synced_at":"2026-02-18T10:00:48.503Z","repository":{"id":90392457,"uuid":"209027563","full_name":"globulus/mmap","owner":"globulus","description":"Enable multiple module support in your JVM annotation processor","archived":false,"fork":false,"pushed_at":"2024-08-17T15:01:25.000Z","size":27,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-24T13:34:49.926Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/globulus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-17T10:43:34.000Z","updated_at":"2024-08-17T15:00:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"f0035ac7-640d-417a-b274-df5e1093fb2b","html_url":"https://github.com/globulus/mmap","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/globulus/mmap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globulus%2Fmmap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globulus%2Fmmap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globulus%2Fmmap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globulus%2Fmmap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/globulus","download_url":"https://codeload.github.com/globulus/mmap/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globulus%2Fmmap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29575343,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T08:38:15.585Z","status":"ssl_error","status_checked_at":"2026-02-18T08:38:14.917Z","response_time":162,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-18T10:00:24.165Z","updated_at":"2026-02-18T10:00:48.488Z","avatar_url":"https://github.com/globulus.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Multi-module Annotation Processing\n\nMMAP allows you to include multiple hierarchical module support in your JVM annotation processor! By default, JVM annotation processors [work on a single module](#why) and don't support hierarchical multi-module projects (i.e those that have several levels of libraries atop the release module). MMAP hacks that, allowing your annotation processor to [preserve its output between module runs](#how-does-it-work) and deliver its result based on data gathered from all the modules' processing.\n\nCheck out [the reasoning behind the lib](#why), [how does it work](#how-does-it-work) and [how to use it](#how-to-use). Additionally, all the Easy libs use it to allow for MM support, such as [EasyPrefs](https://github.com/globulus/EasyPrefs), and [EasyFlavor](https://github.com/globulus/easyflavor).\n\n### Installation\n\nMMAP is [hosted on JCenter](https://bintray.com/beta/#/gordan-glavas/mmap/net.globulus.mmap). To add it, simply include the dependency:\n\n```gradle\n repositories {\n    jcenter()\n}\n...\ndependencies {\n    compile 'net.globulus.mmap:mmap:1.0.1'\n}\n```\n\n### Why\n\nJVM annotation processors work in such a way that each module is processed in complete isolation - the processor doesn't know how many more modules are there out there, and the only output it can produce is a Java file.\n\nThis in itself isn't huge of a restriction and can be worked around, but other limitations exist: e.g, Android DEX requires all the generated file names to be unique, which becomes an issue since the AP can't know the names of previously generated files unless a convention is followed, which then makes it difficult to guarantee unique names for the next module. MMAP solves this issue using timestamps, allowing for unique names across all processed modules, with their inputs properly merged from the top down.\n\n### How does it work\n\nMMAP works by using **merge files**. A merge file is a simple Java class that contains a single byte array containing a serialized **merge input**.\n\nA **merge input** is a class you define yourself, and should represent the input your annotation processor uses to generate code files. The interface itself is very simple, requiring a single method *mergeUp(T)* that merges input from a previous module with the current one - how does that work, what conflict resolution is used, it's all up to you.\n\nAs your annotation processor works, your **MergeManager** will decide if a merge is necessary, and then look for previous merge files, read them, and merge with current input. Then, the new input will be written to new merge files.\n\nMerge files are **guaranteed to have unique names across all modules**, meaning that no name conflict will arise, and your processor's output will work well with Android DEX.\n\n### How to use\n\nThe usage of the lib is extremely simple (code snippets taken from [EasyPrefs](https://github.com/globulus/EasyPrefs)):\n\n1. Define your processor's generated file input as a class that implements **MergeInput**. Implement the *mergeUp* method to define how does the input merge with its top-level input.\n\n```java\npublic static class Input implements MergeInput\u003cInput\u003e {\n    \n    final String masterMethod;\n    final List\u003cPrefType\u003e classes;\n    final List\u003cExposedMethod\u003e methods;\n\n...\n\n    @Override\n    public Input mergedUp(Input other) {\n        String masterMethod = (other.masterMethod != null) ? other.masterMethod : this.masterMethod;\n        List\u003cPrefType\u003e classes = new ArrayList\u003c\u003e(other.classes);\n        classes.addAll(this.classes);\n        List\u003cExposedMethod\u003e methods = new ArrayList\u003c\u003e(other.methods);\n        methods.addAll(this.methods);\n        return new Input(masterMethod, classes, methods);\n    }\n}\n```\n\n2. Create a **MergeManager** instance in your Processor that has your MergeInput class as its type parameter, and supply it the following params:\n\n    * *filer* from your annotation processor.\n    * *timestamp* obtained at the begging of Processor run using *System.currentTimeMillis()*. **It is important that this value be obtained before outside of *Processor#process()* method, ideally in the processor's constructor.**\n    * *packageName* that tells us where should the merge files live.\n    * *processorName* that uniquely identifies your Processor.\n    * *shoulMergeResolver* whose only method decides if your current module should be merged up or not.\n    \n```java\nMergeManager mergeManager =  new MergeManager\u003cInput\u003e(mFiler, mTimestamp,\n                FrameworkUtil.getEasyPrefsPackageName(), NAME,\n                (ShouldMergeResolver) () -\u003e shouldMergeResolution);\n```\n    \n3. Use MergeManager's **manageMerging(T)** method to transform your input to a merged one, and write additional merge classes.\n\n```java\ninput = mergeManager.manageMerging(input);\n```\n\n#### Config\n\nIf you wish to see MMPA's debug output, provide an implementation of a **ProcessorLog** using *MergeManager#setProcessorLog()*.\n\nThe default **lookback period** is 30 seconds - if your machine is slow and the build process for a module takes more than that, i.e subsequent calls to the annotation processor for the next module is more than 30 seconds after the previous one, use *MergeManager#setLookbackPeriod()* to increase this number.\n\n#### Source and Sink\n\nWhen designing your annotation processor, it might be necessary to know if a certain module is the topmost module (i.e, the one at the top of the module hierarchy), or if it is the bottom-most module (i.e, the one that is processed last).\n\nTo make this simpler, MMAP ships with two annotations, *@Source* and *@Sink*, which you may use to these ends. If you decide to employ these annotations, you'll most likely have these two lines in your Processor's *process* method:\n\n ```java\nboolean shouldMerge = roundEnv.getElementsAnnotatedWith(Source.class).isEmpty();\nboolean shouldWriteFinalFile = !roundEnv.getElementsAnnotatedWith(Sink.class).isEmpty();\n```\n\nAgain, how you design your processor and if you use these annotations or not is entire up to the processor developer.\n\n#### Advanced - MergeSession\n\nA MergeSession allows you to do all the steps of a merge individually, as opposed to doing them in a batch via *manageMerging*. You can see a sample use case of this in [EasyFlavor annotation processor](https://github.com/globulus/easyflavor). Here's how to use this feature:\n\n1. Obtain a new MergeSession instance from your MergeManager:\n\n ```java\nMergeSession\u003cMyInput\u003e session = mergeManager.newSession();\n```\n\n2. Call the session actions individually - *mergeInput* finds previous merge files and merges their content with your input, while *writeMergeFiles* writes the provided input to new merge files.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobulus%2Fmmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglobulus%2Fmmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobulus%2Fmmap/lists"}