<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TMate Blog]]></title><description><![CDATA[We are TMate Software, we are experts in version control systems and we create tools that become industry standards, such as SVNKit and SubGit. ]]></description><link>https://blog.tmatesoft.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1606074239879/4DKqMl75p.png</url><title>TMate Blog</title><link>https://blog.tmatesoft.com</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 04:39:00 GMT</lastBuildDate><atom:link href="https://blog.tmatesoft.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Submodules That Don’t Suck]]></title><description><![CDATA[Look out: Git submodules are a trap
It starts out so innocently. 
You have two applications. They share a lot of code. The last thing you want is to waste time developing different versions of that code. You want a way to work on everything together....]]></description><link>https://blog.tmatesoft.com/submodules-that-dont-suck</link><guid isPermaLink="true">https://blog.tmatesoft.com/submodules-that-dont-suck</guid><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[Bitbucket]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Wed, 16 Feb 2022 11:30:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1645030436504/G9v8BwW20r.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-look-out-git-submodules-are-a-trap">Look out: Git submodules are a trap</h2>
<p>It starts out so innocently. </p>
<p>You have two applications. They share a lot of code. The last thing you want is to waste time developing different versions of that code. You want a way to work on everything together.</p>
<p>Well, that’s just what Git submodules are for, right? The shared code has its own repository, which sits inside a parent repository for every application that uses it.</p>
<p>You don’t even have to install anything. It’s already in the tool. You type one command and it’s up and running. Easy peasy.</p>
<p>That might have been as much as you thought about it. After all, this wasn’t the bit that really excited you. Your mind was on what you wanted to build.</p>
<h2 id="heading-it-slowly-dawns-on-you-that-git-is-really-complicated">It slowly dawns on you that Git is really complicated</h2>
<p>And now you – and your whole team – have no choice but to be really good at it.</p>
<p>Much of the difficulty comes down to this: Git submodules don’t just sit inside parent repositories. Every version of the parent repository is tied to a particular version of the submodule.</p>
<p>So, if a developer updates a submodule used in five other repositories, the developer has to update every single one of them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645008644088/zk2w-N6fP.jpeg" alt="Submodules vs X-Modules 1.jpg" /></p>
<p>If that sounds tedious, that’s because it is. But it has to be managed meticulously or projects can break and hours of work can just vanish.</p>
<p>Other times, different sets of changes from different developers wind up in different versions of the submodule, across different instances of the repository. Putting this all together for code review becomes a massive headache.</p>
<p>You might mutter to yourself that the entire point of having version control was to avoid this exact situation. But here you are, juggling multiple versions of the same files.</p>
<h2 id="heading-remembering-the-good-old-days-when-we-all-knew-what-to-type">Remembering the good old days when we all knew what to type</h2>
<p>Suddenly, all the commands you used to know must be done differently and with more of them. Here’s just a taste:</p>
<h3 id="heading-the-familiar-way">The familiar way</h3>
<p>Clone</p>
<pre><code>git <span class="hljs-keyword">clone</span> git:<span class="hljs-comment">//example.org/repos.git</span>
</code></pre><p>Commit</p>
<pre><code>git <span class="hljs-keyword">commit</span>
</code></pre><p>Push</p>
<pre><code>git <span class="hljs-keyword">push</span>
</code></pre><p>Create branch</p>
<pre><code><span class="hljs-attribute">git</span> checkout -b branch
</code></pre><h3 id="heading-the-submodule-way">The submodule way</h3>
<p>Clone</p>
<pre><code>git clone git:<span class="hljs-comment">//example.org/repos.git</span>
git submodule <span class="hljs-keyword">init</span>
git submodule update
</code></pre><p>or</p>
<pre><code>git clone <span class="hljs-operator">-</span><span class="hljs-operator">-</span>recurse <span class="hljs-operator">-</span>submodules
</code></pre><p>Commit</p>
<pre><code>git <span class="hljs-keyword">commit</span> <span class="hljs-comment">/*in each submodule*/</span>
git <span class="hljs-keyword">add</span> <span class="hljs-comment">/*in the parent repository for each module*/</span> 
git <span class="hljs-keyword">commit</span> <span class="hljs-comment">/*in the parent repository*/</span>
</code></pre><p>Push</p>
<pre><code>git push <span class="hljs-comment">/*in each submodule*/</span>
git push <span class="hljs-comment">/*in the parent repository*/</span>
</code></pre><p>Create branch</p>
<pre><code><span class="hljs-comment">/*Create a branch in each submodule and in the parent repository*/</span>
</code></pre><p>This all has to be done precisely, and in the precise order too, or else the problems multiply.</p>
<p>It’s so mundane and repetitive that it feels obvious to just write some quick scripts for and be done with it. Except you’ll never really be done with it. You just have a growing list of scripts that need regular attention as projects grow and change.</p>
<p>There’s no getting around the fact that submodules are a very involved way of thinking about projects, which your whole team has to engage with all the time.</p>
<p>And if you’d rather use a graphical tool like Github Desktop? You might be out of luck. Some tools don’t support submodules well. Others don’t support them at all.</p>
<blockquote>
<h3 id="heading-using-git-submodules-it-doesnt-take-a-big-external-shock-to-derail-projects">Using Git submodules, it doesn’t take a big external shock to derail projects</h3>
<p>All you really need is for someone to be distracted or have a bad day.</p>
</blockquote>
<h2 id="heading-its-an-expensive-way-to-be-miserable">It’s an expensive way to be miserable</h2>
<p>The simple way to start tracking your losses is to add up all the time spent dealing with version control and fixing mistakes. And remember: everyone is losing time to this.</p>
<p>Then ask yourself: what is this doing to your team’s focus and momentum? Their morale and pride in their work?</p>
<p>That’s harder to measure, but it’s still very real.</p>
<h2 id="heading-the-pity-is-that-a-modular-approach-should-be-amazing">The pity is that a modular approach  should be amazing</h2>
<p>There’s so much to like about the idea of separating different projects into their own repository.</p>
<blockquote>
<h3 id="heading-its-fast">It’s fast</h3>
<p>Commands take milliseconds, not minutes.
Developers can search and edit a whole repository on their local machine.</p>
<h3 id="heading-it-scales">It scales</h3>
<p>It grows with you from 3 developers to 3,000 and beyond.
There’s no worry about what happens when 100 developers commit to the same repository at the same time.</p>
<h3 id="heading-its-so-easy-to-share">It’s so easy to share</h3>
<p>It makes it simple to work with external contractors without granting access to all your code.
If you want to publish open-source projects without publishing all of them like Richard Stallman, that’s just as simple.</p>
<h3 id="heading-its-in-one-place">It’s in one place</h3>
<p>You always have a single, authoritative version of the code.</p>
</blockquote>
<p>These are solid benefits. It’s tough to give them up. So, little wonder that Git submodules still have advocates who will tell you to just deal with it and get used to the headaches and mounting technical debt.</p>
<h3 id="heading-but-lets-get-real-about-one-thing">But let’s get real about one thing</h3>
<p>Look, it’s not impossible to find developers who get excited by great version control. I mean, we’re some of them.</p>
<p>But most developers aren’t. They’ve worked too hard to build their own areas of focus.</p>
<p>They’ll deal with version control just enough to hold a job down and avoid disaster. But it’s never going to be the thing they’re passionate about, that inspires them to their very best.</p>
<p>It can be thankless too. About the best they can hope for is to do everything perfectly and then see nobody notice because nothing went wrong.</p>
<p>And really.. are developers even wrong to want this off their plate?</p>
<p>Why shouldn’t they have all their attention on their code?</p>
<h2 id="heading-introducing-git-x-modules">Introducing Git X-Modules</h2>
<p><a target="_blank" href="https://gitmodules.com/">An X-Module</a> sits as an ordinary directory inside a regular Git repository. Developers can treat it just like any other directory, without having to think about it any deeper.</p>
<p>Server-side software keeps this X-Module directory in sync with an external repository, pushing changes in both directions automatically.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645009997599/e-B9PF2P_.jpeg" alt="Submodules vs X-Modules 2.jpg" /></p>
<p>What if there’s a conflict? X-Modules accepts one set of changes, turning the other into a pull request.</p>
<p>And if you don’t want to synchronise certain files? You can exclude them either individually, or by extension. So it’s easy not to have those 7-gigabyte .iso files clogging up the bandwidth.</p>
<blockquote>
<h3 id="heading-code-review-made-simple">Code review made simple</h3>
<p>With X-Modules, all the changes are submitted to one place.
Once they’re approved, they’re synchronised across all repositories automatically.</p>
<h3 id="heading-its-the-modular-monorepo">It’s the modular Monorepo</h3>
<p>You’re welcome to think of X-Modules as a kind of monorepo. Because there really is one big repository that everything synchronises to.
You’re also welcome to treat them as separate repositories. You still enjoy all the speed, scale and simple sharing of a modular approach.</p>
</blockquote>
<h2 id="heading-your-team-already-knows-how-to-use-it">Your team already knows how to use it</h2>
<p>The best thing about X-Modules is that your team doesn’t have to even pay it much notice.</p>
<p>They just interact with it like any other repository, working exactly the same as all the repositories they’ve used before.</p>
<p>All the familiar commands just work. They can use any third-party tools with no special requirements.</p>
<h2 id="heading-so-who-else-wants-the-server-side-solution-that-syncs-your-code-and-stays-out-of-your-way">So who else wants the server-side solution that syncs your code and stays out of your way?</h2>
<p>Git X-Modules is available as a <a target="_blank" href="https://github.com/marketplace/git-x-modules">cloud app for GitHub</a>. You can use it for free while it’s still in beta. Support for GitLab and Bitbucket Cloud will be added soon!
There's also an <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696/git-x-modules-for-bitbucket-server">on-premises app for Bitbucket Data Center</a>.</p>
<p>Seriously, you should try it out.</p>
<hr />
<p><em>Text by <a target="_blank" href="https://www.handsomegenius.com.au/">James Mawson</a></em></p>
<p><em>Illustration by <a target="_blank" href="https://www.freepik.com/vectors/background">pch.vector</a></em></p>
]]></content:encoded></item><item><title><![CDATA[Get your code together!]]></title><description><![CDATA[Whatever could be reused, should be. It's one of the first commandments we learn when we begin to study programming. If you have a reusable piece of code, make it a function; if you have a reusable set of functions, make it a library. If more than on...]]></description><link>https://blog.tmatesoft.com/get-your-code-together</link><guid isPermaLink="true">https://blog.tmatesoft.com/get-your-code-together</guid><category><![CDATA[Git]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Wed, 17 Feb 2021 11:22:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1613560783964/DHd9NiqxR.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Whatever could be reused, should be. It's one of the first commandments we learn when we begin to study programming. If you have a reusable piece of code, make it a function; if you have a reusable set of functions, make it a library. If more than one segment of your project uses it, create a shared library. And if you need to change something in this library... <em>aye, there's the rub</em>.</p>
<p>So what to do with a library, used by several independent services within one project that might be modified by each of them? </p>
<h2 id="approach-1-copynpaste">Approach 1: Copy'n'Paste</h2>
<p>The easiest way is just to place a copy of the library into each project. </p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/5876f4bf7fd2b9f853940f0c5e5f8f1596d74747_2_712x561.png" alt="copypaste|634x499, 75%" /> </p>
<p>However, this is a clear contradiction to the first commandment, which leads to supporting multiple versions of the same library, and is often frowned upon. So we won't do that.</p>
<p>Pros:</p>
<ul>
<li>Simple</li>
</ul>
<p>Cons:</p>
<ul>
<li>Inherently wrong</li>
<li>Creates multiple versions of the same library</li>
</ul>
<h2 id="approach-2-multirepo">Approach 2: Multirepo</h2>
<p>A standard, socially acceptable solution is to keep the shared library in a separate repository and use it as a dependency when necessary. Such setup, when each component has a unique repository, is sometimes referred to as '<strong><em>multirepo</em></strong>'. </p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/b6654c83362fdff4585b0ed6672c9d8ca59f964d_2_775x544.png" alt="multirepo|690x484, 75%" /> </p>
<p>To update a shared component, you just check out that repository, push the updates, then check out your project repository and move on. Some back and forth may happen several times a day (especially if you have more than one shared library), but it's not a nightmare... as long as an interpreted programming language, like JavaScript, is utilized. For compiled languages, such as Java, that use build tools like Maven, the process is more complex:</p>
<ul>
<li>Check out the library repository;</li>
<li>Push the changes;</li>
<li>Have the CI build  the library, test and upload the artifact;</li>
<li>Download the updated library artifact to the project;</li>
<li>Test if your updates  work;</li>
<li>If not - go back to the first step.</li>
</ul>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/c7eb4aaafe3e5edc8657bfa7fa1939d74ba93327_2_811x750.png" alt="multirepo update cycle new|541x500" /> </p>
<p>Pros: </p>
<ul>
<li>Simple</li>
</ul>
<p>Cons:</p>
<ul>
<li>Updating shared components and the main project in parallel is troublesome</li>
</ul>
<h2 id="approach-3-git-submodules">Approach 3: Git Submodules</h2>
<p>Invented by the best minds in modern software engineering, Git has a solution to this, called '<strong><em>git submodules</em></strong>'. It allows embedding one repository inside another, like Russian nesting dolls.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/4425c915d6fefab4dff1ca5c835693daf4fd33d5_2_729x562.png" alt="submodules|649x500, 75%" /> </p>
<p>However, if you search, what people say about it, you will find a lot of comments like that:</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/fdaac8817398d055b1c961b58ecf294582092358.png" alt /></p>
<p>What's the catch? It's in the nature of git submodules: each submodule remains a separate repository. You don't have to check it out separately, but you can't treat it as part of the main repository. If one of the updated files belongs to a submodule, update the submodule first with a special command, then commit the new dependency to the project repository, and only then commit the rest of the update. Doing it in the wrong way will break it all, and may even <a target="_blank" href="https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/">cause you to lose your code</a>.</p>
<p>Pros: </p>
<ul>
<li>Native Git solution</li>
<li>All code is in one directory</li>
</ul>
<p>Cons:</p>
<ul>
<li>Client-side - each developer has to set up and maintain it for himself;</li>
<li>Need special commands to work with module (shared) repositories;</li>
<li>Could be easily broken.</li>
</ul>
<h2 id="approach-4-monorepo">Approach 4: Monorepo</h2>
<p>There were numerous attempts to improve Git Submodules ('git subtree', 'git subrepo' - to name a few), but in the past several years the industry seems to have abandoned this. Large companies, such as Google, Facebook, and Twitter, started gathering all their codebases in massive repositories, so-called "<strong><em>monorepos</em></strong>". </p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/cda7f91e10a68c33bfe0f6cc43d1278e46f8d35c_2_267x562.png" alt="monorepo|238x500, 75%" /> </p>
<p>They tried to remedy the problem by storing all projects and libraries in the same repository. <a target="_blank" href="https://medium.com/%40mattklein123/monorepos-please-dont-e9a279be011b">Instead, they got</a>  enormous repositories, where <code>git status</code> took minutes, and <code>git clone</code> might have taken hours. Another issue was the organizational mess when hundreds of developers simultaneously committed to the same repository.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/8dcb24c0c03189def63e8a7f26666aa0f09e538d.png" alt="B4XcYicCIAA5SLl|413x360" /> 
(<a target="_blank" href="https://twitter.com/monorepi">source</a>)</p>
<p>So the next step was to develop sophisticated tools ('Basel' for Google, 'Pants' for Twitter) to make monorepos function.</p>
<p>Pros:</p>
<ul>
<li>All code (in theory) accessible to every project</li>
</ul>
<p>Cons:</p>
<ul>
<li>Scalability</li>
<li>Hardware resources gluttony</li>
<li>Needs special tooling</li>
</ul>
<h2 id="approach-5-git-x-modules">Approach 5: Git X-Modules</h2>
<p>While dozens of engineers struggle online in the monorepo/multirepo battles, another approach is possible -- <strong><em><a target="_blank" href="https://gitmodules.com">Git X-Modules</a></em></strong>. It's similar to Git Submodules, but has a very important difference: the repositories are nested on the server's side. </p>
<p>For the end-user, it's just a regular repository, with all code needed for his or her project (and nothing except that). He or she makes all updates with one atomic commit to such repository, and they are distributed automatically on the server between the project and the shared libraries.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/2ebf1fcf2d0406a80f365ceb626f3237ce8b6f2e_2_775x544.png" alt="x-modules 1|690x485, 75%" /> </p>
<p>In fact, one can build a monorepo this way as well -- keeping all original repositories independent and intact.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/94211c718e86f50c7f3f909f22998fdbe2a8cc5b_2_799x750.png" alt="x-modules 2|533x500, 100%" /> </p>
<p>This software is still in the beta stage, however, a command-line tool and a plugin for Bitbucket Server/Data Center are <a target="_blank" href="https://gitmodules.com">available for download</a>. Other implementations are coming soon.</p>
<p>Pros:</p>
<ul>
<li>No overhead to end-users - only standard Git commands</li>
<li>Leaves the original repositories intact</li>
<li>Can combine complete repositories or parts of them</li>
</ul>
<p>Cons:</p>
<ul>
<li>Beta stage</li>
<li>So far - only available for self-hosted Git servers</li>
</ul>
<p>Now, what is your opinion? Which approach is more likely to lead, and why? What's your story of working with code, split between multiple repositories?</p>
]]></content:encoded></item><item><title><![CDATA[Embedded development project structure with Git X-Modules]]></title><description><![CDATA[Embedded development, just like any other, often depends on shared code components such as libraries for specific hardware. However, there's no de-facto industry standard for managing modular projects in this area.  We are going to describe one of th...]]></description><link>https://blog.tmatesoft.com/embedded-development-project-structure-with-git-x-modules</link><guid isPermaLink="true">https://blog.tmatesoft.com/embedded-development-project-structure-with-git-x-modules</guid><category><![CDATA[Git]]></category><category><![CDATA[embedded]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Sat, 19 Dec 2020 14:45:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1608316669291/mDz5Axs5a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Embedded development, just like any other, often depends on shared code components such as libraries for specific hardware. However, there's no de-facto industry standard for managing modular projects in this area.  We are going to describe one of the ways to do it on a Git repository level.   We will mostly concentrate on project organization topics rather than on particular questions of embedded development.</p>
<h2 id="infrastructure">Infrastructure</h2>
<p>As an example,  we will create a simple program that blinks the LEDs of <a target="_blank" href="https://www.st.com/en/evaluation-tools/stm32f4discovery.html">STM32F4DISCOVERY</a> board by STMicroelectronics.  I assume we have a STM32F4DISCOVERY board with <a target="_blank" href="https://www.st.com/en/microcontrollers-microprocessors/stm32f407-417.html">ST32F407VGT6</a> MCU. I'm using Debian 10.6, but for any other OS, the steps are similar. I have 'stlink-tools' and 'gcc-arm-none-eabi' packages installed. The former is a collection of tools (and 'st-flash' in particular) to work with the Discovery board (e.g. 'st-flash' writes the compiled binary file to the MCU's flash using STLink protocol). The latter is a generic ARM cross-compiler as ST32F407VGT6 is ARM Cortex M4 based MCU.</p>
<p> If you don't have these packages installed do that now:</p>
<pre><code> <span class="hljs-attribute">sudo</span> apt install stlink-tools gcc-arm-<span class="hljs-literal">none</span>-eabi
</code></pre><p> STMicroelectronics provides a standard library for ST32F407VGT6 (<a target="_blank" href="https://www.st.com/content/st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries/stsw-stm32065.html">STSW-STM32065</a>). But instead of using it (not everybody is happy with its APIs), we will develop our own library by using MCU datasheets directly. Another reason to develop our own library is to demonstrate a typical scenario when libraries are developed together with the main project itself and can be reused in other projects.</p>
<p> Although we will have only one project in this post, we will assume there're other projects reusing the same library. In particular, fixes and updates to the library should get into these projects as well.</p>
<p> We will assume the code to be stored in Git and Atlassian <a target="_blank" href="https://www.atlassian.com/software/bitbucket/download">Bitbucket Server</a>/<a target="_blank" href="https://www.atlassian.com/enterprise/data-center/bitbucket">Data Center</a> is used on the server's side. This is not necessary but would give us a nice UI.</p>
<h2 id="project-structure">Project Structure</h2>
<p>Our project will consist of the following major components:</p>
<ul>
<li>main project source file;</li>
<li>generic library supporting ST32F407VGT6 MCU;</li>
<li>files and compiler options needed to cross-compile the C code to the ARM platform.</li>
</ul>
<p>To cross-compile a project, one has to use a number of specific compiler options and also a startup and a linker script file. When having several projects for the same MCU, it makes sense to share these files and options across projects. Thus we will have a common component dedicated to these compilation-related files. We will name this component <strong>'common'</strong>.</p>
<p> We will name the library <strong>'stm32'</strong> component. The main project will be named <strong>'embedded-example'</strong>.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/0e1e75346a40b9f94e57ec1c670fd58ddccddb25.png" alt="01-structure|690x269" /> </p>
<h2 id="step-1-create-the-stm32-project">Step 1. Create the 'stm32' project</h2>
<p>Create 'stm32' Git repositrory with Atlassian Bitbucket Server/Data Center UI 
<img src="https://support.tmatesoft.com/uploads/default/original/1X/873247724cc29028714b3a4528c32ef6c72b1b8b.png" alt="02-create-stm32-repository|653x500" /> 
and clone it.</p>
<pre><code><span class="hljs-attribute">git</span> clone http://example.org/scm/ee/stm<span class="hljs-number">32</span>.git stm<span class="hljs-number">32</span>/
<span class="hljs-attribute">cd</span> stm<span class="hljs-number">32</span>/
</code></pre><p>Create CMakeLists.txt file there:</p>
<pre><code><span class="hljs-attribute">cmake_minimum_required</span>(VERSION <span class="hljs-number">3</span>.<span class="hljs-number">8</span> FATAL_ERROR)
<span class="hljs-attribute">project</span>(stm<span class="hljs-number">32</span>)

<span class="hljs-attribute">add_library</span>(stm<span class="hljs-number">32</span> pin.c gpio.c util.c)
<span class="hljs-attribute">target_include_directories</span>(stm<span class="hljs-number">32</span> PUBLIC .)
</code></pre><p>This CMakeLists.txt file describes a project with three C files and tells all the projects that would include it that the include directory is at the project root. So the files structure will be:</p>
<pre><code>├── <span class="hljs-selector-tag">CMakeLists</span><span class="hljs-selector-class">.txt</span>
├── <span class="hljs-selector-tag">gpio</span><span class="hljs-selector-class">.c</span>
├── <span class="hljs-selector-tag">gpio</span><span class="hljs-selector-class">.h</span>
├── <span class="hljs-selector-tag">pin</span><span class="hljs-selector-class">.c</span>
├── <span class="hljs-selector-tag">pin</span><span class="hljs-selector-class">.h</span>
├── <span class="hljs-selector-tag">util</span><span class="hljs-selector-class">.c</span>
└── <span class="hljs-selector-tag">util</span><span class="hljs-selector-class">.h</span>
</code></pre><p>Now create these files and their headers: </p>
<ul>
<li><a target="_blank" href="https://support.tmatesoft.com/uploads/short-url/yECbnDvc85xTjha0nzKwYJj2bT8.h">util.h</a> &amp; <a target="_blank" href="https://support.tmatesoft.com/uploads/short-url/iySFXJyPbs5GgowkwUCfysOnJu7.c">util.c</a> - they contain convenience functions to set and get special bits;</li>
<li><a target="_blank" href="https://support.tmatesoft.com/uploads/short-url/iDFjZCjuxX6sz1c5qP6oGKUe7X0.h">gpio.h</a> &amp; <a target="_blank" href="https://support.tmatesoft.com/uploads/short-url/wJaspm7z1Pl1q178PeUUXKSebfM.c">gpio.c</a> - they contain a dull and straightforward implementation of ST32F407VGT6 datasheet GPIO specifications;</li>
<li><a target="_blank" href="https://support.tmatesoft.com/uploads/short-url/t3MMaCZjA7qrduIpmzeCcwyGYBs.h">pin.h</a> &amp; <a target="_blank" href="https://support.tmatesoft.com/uploads/short-url/wV6KyqgSBTju41yXc2ggYCF9u0p.c">pin.c</a> - they define pin names like <code>hw_pin_PD12</code> and their convenience equivalents like <code>hw_pin_led_green</code>. </li>
</ul>
<p>For instance, on STM32F4DISCOVERY PD12 pin of the MCU is connected to the green LED, that's why it's convenient to define:</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> hw_pin_led_green hw_pin_PD12</span>
</code></pre><p>Now add, commit, and push the changes to 'stm32' library:</p>
<pre><code>git <span class="hljs-keyword">add</span> *.c
git <span class="hljs-keyword">add</span> *.h
git <span class="hljs-keyword">add</span> CMakeLists.txt
git <span class="hljs-keyword">commit</span> -m "Initial."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/e1dc77244270992b30b3b750ff9c9907265f4c95.png" alt="03-stm32-repository-toc|436x499" /> 
I would note that as this library is self-contained, one can even compile (but not cross-compile, so far) it:</p>
<pre><code>mkdir build
<span class="hljs-built_in">cd</span> build
cmake ..
make
</code></pre><p>If everything compiles, it's a good indicator, though a pretty useless property as our target platform is ARM.</p>
<h2 id="step-2-create-the-common-project">Step 2. Create the 'common' project</h2>
<p>This project will contain the common configuration that is shared between projects. It's also convenient to put it into a separate Git repository and insert it into each project repository to be included by the project CMakeLists.txt.</p>
<p>Create  the 'common' repository with Atlassian Bitbucket Server/Data Center </p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/15877b568888b77d3eb7b948b045d42fe461e0a9.png" alt="04-create-common-repository|659x500" /> 
and clone the newly created repository:</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/ee/common.git common/</span>
cd common/
</code></pre><p>Now create <code>vars.cmake</code>:</p>
<pre><code><span class="hljs-keyword">set</span>(CMAKE_SYSTEM_NAME      Generic)
<span class="hljs-keyword">set</span>(CMAKE_SYSTEM_VERSION   <span class="hljs-number">1</span>)
<span class="hljs-keyword">set</span>(CMAKE_SYSTEM_PROCESSOR arm-eabi)

<span class="hljs-keyword">set</span>(CMAKE_C_COMPILER       arm-<span class="hljs-keyword">none</span>-eabi-gcc)
<span class="hljs-keyword">set</span>(CMAKE_CXX_COMPILER     arm-<span class="hljs-keyword">none</span>-eabi-g++)
<span class="hljs-keyword">set</span>(CMAKE_ASM_COMPILER     arm-<span class="hljs-keyword">none</span>-eabi-<span class="hljs-keyword">as</span>)
<span class="hljs-keyword">set</span>(CMAKE_OBJCOPY          arm-<span class="hljs-keyword">none</span>-eabi-objcopy)
<span class="hljs-keyword">set</span>(CMAKE_OBJDUMP          arm-<span class="hljs-keyword">none</span>-eabi-objdump)

<span class="hljs-keyword">set</span>(CMAKE_C_FLAGS <span class="hljs-string">"-mthumb -mcpu=cortex-m4 -fno-builtin -Wall -Wno-pointer-to-int-cast -std=gnu99 -fdata-sections -ffunction-sections"</span> <span class="hljs-keyword">CACHE</span> INTERNAL <span class="hljs-string">"c compiler flags"</span>)
<span class="hljs-keyword">set</span>(CMAKE_CXX_FLAGS <span class="hljs-string">"-mthumb -mcpu=cortex-m4 -fno-builtin -Wall -Wno-pointer-to-int-cast -fdata-sections -ffunction-sections"</span> <span class="hljs-keyword">CACHE</span> INTERNAL <span class="hljs-string">"cxx compiler flags"</span>)
<span class="hljs-keyword">set</span>(CMAKE_ASM_FLAGS <span class="hljs-string">"-mthumb -mcpu=cortex-m4"</span> <span class="hljs-keyword">CACHE</span> INTERNAL <span class="hljs-string">"asm compiler flags"</span>)

<span class="hljs-keyword">set</span>(CMAKE_EXE_LINKER_FLAGS <span class="hljs-string">"-nostartfiles -Wl,--gc-sections -mthumb -mcpu=cortex-m4"</span> <span class="hljs-keyword">CACHE</span> INTERNAL <span class="hljs-string">"exe link flags"</span>)
<span class="hljs-keyword">set</span>(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS <span class="hljs-keyword">CACHE</span> INTERNAL <span class="hljs-string">"reset default flags"</span>)

<span class="hljs-keyword">set</span>(STM32_STLINK_CLI_EXECUTABLE <span class="hljs-string">"st-flash"</span>)
</code></pre><p>This file defines various variables needed for the cross-compilation of sources with ARM as the target platform. It sets compiler flags and a path to the 'st-flash' utility that writes binary files to MCU.</p>
<p>Also add <code>stm32f407vg_flash.ld</code>, <code>startup_stm32f40xx.s</code>, and <code>stm32f4xx.h</code> files. I will not cite them here, but they are quite standard and distributed by STMicroelectronics.</p>
<p>So the repository content becomes:</p>
<pre><code>├── <span class="hljs-selector-tag">startup_stm32f40xx</span><span class="hljs-selector-class">.s</span>
├── <span class="hljs-selector-tag">stm32f407vg_flash</span><span class="hljs-selector-class">.ld</span>
├── <span class="hljs-selector-tag">stm32f4xx</span><span class="hljs-selector-class">.h</span>
└── <span class="hljs-selector-tag">vars</span><span class="hljs-selector-class">.cmake</span>
</code></pre><p>Commit and push the changes:</p>
<pre><code><span class="hljs-attribute">git</span> add vars.cmake stm<span class="hljs-number">32</span>f<span class="hljs-number">4</span>xx.h startup_stm<span class="hljs-number">32</span>f<span class="hljs-number">40</span>xx.s stm<span class="hljs-number">32</span>f<span class="hljs-number">407</span>vg_flash.ld
<span class="hljs-attribute">git</span> commit -m <span class="hljs-string">"Initial."</span>
<span class="hljs-attribute">git</span> push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/b11be8ad976df1d692a6da47b61acea7545c29e1.png" alt="05-common-repository-toc|576x500" /> </p>
<h2 id="step-3-the-project-repository">Step 3. The project repository</h2>
<p>Now let's create the main project repository. The repository will have the following structure:</p>
<pre><code>├── CMakeLists.txt
├── common   &lt;<span class="hljs-comment">--- here common.git will be inserted</span>
├── libs
│   └── stm32  &lt;<span class="hljs-comment">--- here stm32.git will be inserted</span>
│
└── src
    └── main.c
</code></pre><p>So create an empty Git repository using Atlassian Bitbucket Server/Data Center UI</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/d976265ffdf0b669d888fb3b40eb5424eae003bc.png" alt="06-create-embedded-example-repository|660x500" /> 
and clone it:</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/ee/embedded-example.git embedded-example/</span>
cd embedded-example/
</code></pre><p>Create <code>CMakeLists.txt</code> there:</p>
<pre><code>cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
include(common/vars.cmake)
project(embedded-example)
enable_language(C ASM)

add_subdirectory(libs/stm32)

<span class="hljs-built_in">set</span>(CMAKE_EXE_LINKER_FLAGS <span class="hljs-string">"<span class="hljs-variable">${CMAKE_EXE_LINKER_FLAGS}</span> -T<span class="hljs-variable">${CMAKE_SOURCE_DIR}</span>/common/stm32f407vg_flash.ld"</span>)

add_executable(<span class="hljs-variable">${PROJECT_NAME}</span>.elf
    <span class="hljs-variable">${CMAKE_SOURCE_DIR}</span>/common/startup_stm32f40xx.s
    <span class="hljs-variable">${CMAKE_SOURCE_DIR}</span>/src/main.c)
target_include_directories(<span class="hljs-variable">${PROJECT_NAME}</span>.elf PRIVATE stm32)
target_link_libraries(<span class="hljs-variable">${PROJECT_NAME}</span>.elf PRIVATE stm32)

add_custom_target(<span class="hljs-variable">${PROJECT_NAME}</span>.hex DEPENDS <span class="hljs-variable">${PROJECT_NAME}</span>.elf COMMAND <span class="hljs-variable">${CMAKE_OBJCOPY}</span> -Oihex <span class="hljs-variable">${PROJECT_NAME}</span>.elf <span class="hljs-variable">${PROJECT_NAME}</span>.hex)
add_custom_target(<span class="hljs-variable">${PROJECT_NAME}</span>.bin DEPENDS <span class="hljs-variable">${PROJECT_NAME}</span>.elf COMMAND <span class="hljs-variable">${CMAKE_OBJCOPY}</span> -Obinary <span class="hljs-variable">${PROJECT_NAME}</span>.elf <span class="hljs-variable">${PROJECT_NAME}</span>.bin)
<span class="hljs-built_in">set</span>(STLINK_CMD <span class="hljs-variable">${STM32_STLINK_CLI_EXECUTABLE}</span> write <span class="hljs-variable">${CMAKE_BINARY_DIR}</span>/<span class="hljs-variable">${PROJECT_NAME}</span>.bin 0x8000000)
add_custom_target(write-flash DEPENDS <span class="hljs-variable">${PROJECT_NAME}</span>.bin COMMAND <span class="hljs-variable">${STLINK_CMD}</span>)
</code></pre><p>It's important to call <code>include()</code> on the file with common variables before <code>project()</code> call otherwise CMake falls into an infinite loop (I would consider this strange behavior as a CMake bug). It's also important to specify the linker script that we've put into the 'common.git' project.</p>
<p><code>add_subdirectory()</code> call includes the library. We can include several libraries there. The libraries will be inserted in the <code>libs/</code> directory.</p>
<p>The last line creates a <code>make write-flash</code> target that will not only create a binary file for the MCU but also will write it into its flash using STLink protocol. <code>${STM32_STLINK_CLI_EXECUTABLE}</code> is defined in the 'common' project.</p>
<p>Now create <code>src/main.c</code> file:</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;gpio.h&gt;</span></span>

<span class="hljs-keyword">hw_pin_t</span> leds[] = {
  hw_pin_led_red,
  hw_pin_led_green,
  hw_pin_led_blue,
<span class="hljs-comment">//  hw_pin_led_orange</span>
};

<span class="hljs-keyword">int</span> leds_count = <span class="hljs-keyword">sizeof</span>(leds) / <span class="hljs-keyword">sizeof</span>(<span class="hljs-keyword">hw_pin_t</span>);

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">SystemInit</span><span class="hljs-params">()</span> </span>{
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
  hw_gpio_configure(hw_pin_led_blue, hw_gpio_mode_output_pull_push, hw_gpio_speed_2mhz, hw_gpio_alternate_function_none);
  hw_gpio_configure(hw_pin_led_green, hw_gpio_mode_output_pull_push, hw_gpio_speed_2mhz, hw_gpio_alternate_function_none);
  hw_gpio_configure(hw_pin_led_orange, hw_gpio_mode_output_pull_push, hw_gpio_speed_2mhz, hw_gpio_alternate_function_none);
  hw_gpio_configure(hw_pin_led_red, hw_gpio_mode_output_pull_push, hw_gpio_speed_2mhz, hw_gpio_alternate_function_none);

  <span class="hljs-keyword">int</span> current_led = <span class="hljs-number">0</span>;

  <span class="hljs-keyword">while</span> (TRUE) {  
    hw_gpio_set(leds[current_led], FALSE);
    current_led = (current_led + <span class="hljs-number">1</span>) % leds_count;
    hw_gpio_set(leds[current_led], TRUE);

    <span class="hljs-keyword">int</span> delay = <span class="hljs-number">1000000</span>;
    <span class="hljs-keyword">while</span> (delay--) {
    }
  }
}
</code></pre><p>This file is simple: it initializes pins related to LEDs and blinks with red, green, and blue pins. We've commented out the orange pin as default STM32F4DISCOVERY firmware blinks with all 4 LEDs and we want to differ from that.</p>
<p>Commit and push the changes:</p>
<pre><code>git <span class="hljs-keyword">add</span> src/main.c
git <span class="hljs-keyword">add</span> CMakeLists.txt
git <span class="hljs-keyword">commit</span> -m "Initial."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/d9c915116b11961601265883adb33a94552798b9.png" alt="07-embedded-example-repository-toc|682x500" /> </p>
<h2 id="step-4-insert-commongit-and-stm32git-repositories-into-embedded-examplegit">Step 4. Insert 'common.git' and 'stm32.git' repositories into 'embedded-example.git'</h2>
<p>To insert one repository to another we will use Git <a target="_blank" href="http://tmatesoft.com/x-modules/">X-Modules</a>. For Atlassian Bitbucket Server/Data Center there's a dedicated app with a nice UI. For other Git servers use the: Git X-Modules Command-Line Tool.</p>
<p>Make sure you have X-Modules app installed into Atlassian Bitbucket Server/Data Center. If not, visit <code>Administration | Find new apps | Search the [Marketplace](https://marketplace.atlassian.com/apps/1223696/git-x-modules-for-bitbucket-server)</code> and type "X-Modules" from Bitbucket Server/Data Center UI.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7c874cca26baf6d433d63b89b39b0eedf20c36d1.png" alt="07-install-x-modules-app|690x203" /> 
Go to  the 'embedded-example' Git repository page. When the Git X-Modules app is installed there's  a Git X-Modules button on the left panel, click it.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/30c200fa3dd3234e27f1782f55b570513b4dbce7.png" alt="09-x-modules-button|690x181" /> 
Then click 'Add Module' to add the first module (let it be 'common.git').
<img src="https://support.tmatesoft.com/uploads/default/original/1X/46e007817169e53196cb4ad23826dfded5c33371.png" alt="11-x-modules-add-first-module|690x275" /> 
Choose 'common' repository and 'master' branch. Make sure "This Repository Path:" is 'common'. It's the path where the repository will be inserted:
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7dd992cca949dbd4265d54a9ec7e16612197263f.png" alt="11-x-modules-add-common-preview|690x443" /> 
Click 'Add Module'. Without applying the changes click 'Add Module' again to add 'stm32' repository as module.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/c3d9f36974d6ee31ce79b5c41e851ea4b2636ab3_2_1035x633.png" alt="12-x-modules-add-second-module|690x422" /> 
Choose 'stm32' repository and 'master' branch.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/0e80fd157ab784d46ac28125e52195f58ac7978e.png" alt="13-x-modules-choose-stm32-repository|690x464" /> 
Make sure "This Repository Path:" is "libs/stm32", this is the insertion path for 'stm32.git' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/78ae9da034caa573884d7a570a3c564c6579a09b.png" alt="14-x-modules-choose-stm32-module-path|690x453" /> 
Click 'Add Module' and apply the changes.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/fd6e837b51aee41a2c17c4f048075f2a94bfe91b.png" alt="15-x-modules-apply-changes|664x500" /> 
Now the repositories are inserted as X-Modules.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/1b622d36989c04a8e3ad8a1fa541c29660cb37ee.png" alt="16-embedded-example-repository-toc|560x500" /> 
This means that 'common.git' and 'stm32.git' are synchronized with corresponding directories ('common' and 'libs/stm32' respectively) of 'embedded-example.git'.</p>
<p>Fetch the changes from 'embedded-example':</p>
<pre><code>cd embedded-example/
git pull <span class="hljs-comment">--rebase</span>
</code></pre><p>Now the project contains everything:</p>
<pre><code>├── <span class="hljs-selector-tag">CMakeLists</span><span class="hljs-selector-class">.txt</span>
├── <span class="hljs-selector-tag">common</span>
│   ├── <span class="hljs-selector-tag">startup_stm32f40xx</span><span class="hljs-selector-class">.s</span>
│   ├── <span class="hljs-selector-tag">stm32f407vg_flash</span><span class="hljs-selector-class">.ld</span>
│   ├── <span class="hljs-selector-tag">stm32f4xx</span><span class="hljs-selector-class">.h</span>
│   └── <span class="hljs-selector-tag">vars</span><span class="hljs-selector-class">.cmake</span>
├── <span class="hljs-selector-tag">libs</span>
│   └── <span class="hljs-selector-tag">stm32</span>
│       ├── <span class="hljs-selector-tag">CMakeLists</span><span class="hljs-selector-class">.txt</span>
│       ├── <span class="hljs-selector-tag">gpio</span><span class="hljs-selector-class">.c</span>
│       ├── <span class="hljs-selector-tag">gpio</span><span class="hljs-selector-class">.h</span>
│       ├── <span class="hljs-selector-tag">pin</span><span class="hljs-selector-class">.c</span>
│       ├── <span class="hljs-selector-tag">pin</span><span class="hljs-selector-class">.h</span>
│       ├── <span class="hljs-selector-tag">util</span><span class="hljs-selector-class">.c</span>
│       └── <span class="hljs-selector-tag">util</span><span class="hljs-selector-class">.h</span>
└── <span class="hljs-selector-tag">src</span>
    └── <span class="hljs-selector-tag">main</span><span class="hljs-selector-class">.c</span>
</code></pre><p>Check that the project compiles:</p>
<pre><code>mkdir build
cd build
cmake ..
<span class="hljs-built_in">make</span>
<span class="hljs-built_in">make</span> write-flash
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/5cfee0d23ea8aa56fd5058d3f1a325d5284c198e.gif" alt />
At the moment of running <code>make write-flash</code>, the STM32F4DISCOVERY board should be connected. If you do everything correctly, you'll see 3 LEDs blinking.</p>
<p>This repository structure can be reused for any other ST32F407VGT6-based project, it's enough to insert 'common' and 'stm32' at the corresponding places. Moreover, the directories are bi-directionally synchronized with the inserted repositories, so one can change, see the results, and modify 'stm32' directly from the project repository.</p>
]]></content:encoded></item><item><title><![CDATA[Develop an Android library together with apps using it]]></title><description><![CDATA[Imagine that you are developing a taxi service like Uber, with two separate apps (one for passengers, another for drivers). However, they are might be using the same shared libraries (e.g. for network protocols or UI animation), and both teams need a...]]></description><link>https://blog.tmatesoft.com/develop-an-android-library-together-with-apps-using-it</link><guid isPermaLink="true">https://blog.tmatesoft.com/develop-an-android-library-together-with-apps-using-it</guid><category><![CDATA[android apps]]></category><category><![CDATA[Android Studio]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Mon, 30 Nov 2020 10:57:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606685332034/3K9wmqjPV.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine that you are developing a taxi service like Uber, with two separate apps (one for passengers, another for drivers). However, they are might be using the same shared libraries (e.g. for network protocols or UI animation), and both teams need access to it. Did you know, that monorepo is not the only way to handle this?</p>
<p>This post describes a way to organize sources of 2 Android apps ('<strong>app1</strong>' and '<strong>app2</strong>') using the same common library ('<strong>commonlib</strong>'). The configuration will allow one to work on the library sources together with both apps. </p>
<p><strong>Why do you need this?</strong> 
A common approach would be to put the common library to the Maven and to use it as a binary dependency for each of the apps. This approach makes it difficult to quickly test changes in the library code: to do that one has to</p>
<ul>
<li>fix the 'commonlib',</li>
<li>build and upload changes to the Maven (often this is done by CI and involves runnings tests),</li>
<li>obtain the updated artifact from the 'app1' or 'app2' project, </li>
<li>and, finally, build and re-run the app to see what's different now.</li>
</ul>
<p>If the changes do not work as intended, one has to repeat.</p>
<p>Another approach would be to put the 'commonlib', code into the same repository and project as 'app1' or 'app2'. With just one app that would have worked. But with two or more apps it would be an obvious code duplication and therefore not an option.</p>
<p>Finally, there's the "monorepo" approach: put all code - ('app1', 'app2', and 'commonlib') into the same Git repository and manage it together. But, besides other well-known <a target="_blank" href="https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b">drawbacks of the monorepo approach</a>, it's the best practice to place all code components inside one project directory - yet the 'commonlib' can't be inside the same directory with 'app1' and 'app2' at the same time</p>
<p><strong>The solution sketch.</strong> 
We will adopt a modification of the second approach because it's natural to the Android Studio. When you create an Android library in the Android Studio, it puts the library as another module into the same app project.</p>
<p>Android app projects are usually built with Gradle. They are organized as Gradle projects containing Gradle modules. One of the modules is the app module itself, while libraries it uses could be added as separate modules (together with their sources if developed simultaneously with the app itself) or as binary Maven dependencies (and then one cannot change their sources on the fly).</p>
<p>In our example, we will create 2 projects: 'app1' and 'app2'. Each of them would contain an app-related module (named 'app' by default) and a 'commonlib' module with the sources of the library. We just need to make sure that the 'commonlib' module of both 'app1' and 'app2' projects uses the same sources.</p>
<p>A possible solution would be to put the 'commonlib' module into a separate Git repository and insert it as a Git <a target="_blank" href="https://git-scm.com/book/en/v2/Git-Tools-Submodules">submodule</a> into the other two Git repositories: 'app1' and 'app2' (corresponding to Gradle projects with the same names). But you know...</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/fdaac8817398d055b1c961b58ecf294582092358.png" alt="02-submodules-tweets|690x250" /> </p>
<p>Git submodules have <a target="_blank" href="https://ayende.com/blog/4746/the-problem-with-git-submodules">too</a> <a target="_blank" href="https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/">many</a> <a target="_blank" href="https://medium.com/@uttamkini/sharing-code-and-why-git-submodules-is-a-bad-idea-1efd24eb645d">disadvantages</a>.</p>
<p>So we will use <a target="_blank" href="https://tmatesoft.com/x-modules">Git X-Modules</a> instead. This is a drop-in replacement for Git submodules,  working at the server's side. From the Git perspective, they are just regular directories.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/3c29768a31bb89eed1ef8440373959e0b684680b.png" alt="29-structure|690x486" /></p>
<p><strong>Infrastructure.</strong> For the purpose of this post I will use Atlassian <a target="_blank" href="https://www.atlassian.com/software/bitbucket/download">Bitbucket Server</a>/<a target="_blank" href="https://www.atlassian.com/enterprise/data-center/bitbucket">Data Center</a> as Git server software. It's one of the most popular self-hosted Git solutions and Git X-Modules has a <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696/">dedicated app with a nice UI</a> for it. Yet the same solution would also work for almost any other Git server software - there's a command-line version of it with the same capabilities, just without the GUI.</p>
<p>I'm using Android Studio 4.4.1 and Gradle 6.7.1. Both are the latest versions to date.</p>
<p><strong>Step 1.</strong> Create the 'app1' project.</p>
<p>Create a new project, choose "Basic Activity" as the template.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/0d96d9bab41986cf1794a5369d6bea03adb5ebdd.png" alt="01-choose-template|656x500" /> 
Choose the name of the app (App1), click Finish.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c99a895ac0982e0b86a3e32ff777107d58335479.png" alt="02-choose-app1-name|663x500" /> 
Run the project to make sure everything works smoothly.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/84250dd8134e96852978d1f976978481f1f1fc9e_2_1035x691.png" alt="03-make-sure-app1-can-be-built|690x461" /> </p>
<p><strong>Step 2.</strong> Create the 'commonlib' library.</p>
<p>Choose <code>File | New | New Module...</code>
<img src="https://support.tmatesoft.com/uploads/default/original/1X/4b251feedf0ee98865ac9162500d3d69ea80c42a.png" alt="04-create-new-module|465x499" /> 
Choose "Android library" as the module type.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/1af31d8943b92dae0c2c77c8e7236a4aceecfc80.png" alt="05-choose-module-type-library|663x500" /> 
Choose the module name: 'commonlib'.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/f9031781266c911f72ce442083a2a30e7549259b.png" alt="06-choose-commonlib-name|664x500" /> 
Create CommonLib class in the 'commonlib' module with a method.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/9b27c659a10af6d1e89a67ed8c5a0e1f83d58923.png" alt="07-create-commonlib-class|616x500" /> </p>
<p>Add a dependency for the default ('app') module on the 'commonlib' module and use the method in the application.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/f7c54b880f0683fa17b7d70d8b3513e9be4d9601.png" alt="08-use-commonlib-in-app1|689x396" /> 
Run the application to make sure everything works smoothly.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/eb226a368c8018144f5068c3ea7690d53cd4569a.png" alt="09-app1-with-commonlib-emulator|235x500" /> </p>
<p><strong>Step 3.</strong> Create 'app1' and 'commonlib' Git repositories.</p>
<p>The 'App1' project now has the following structure:</p>
<pre><code>├── <span class="hljs-selector-tag">app</span>
├── <span class="hljs-selector-tag">build</span><span class="hljs-selector-class">.gradle</span>
├── <span class="hljs-selector-tag">commonlib</span>     &lt;<span class="hljs-selector-tag">---</span> <span class="hljs-selector-tag">this</span> <span class="hljs-selector-tag">should</span> <span class="hljs-selector-tag">go</span> <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">commonlib</span><span class="hljs-selector-class">.git</span>
├── <span class="hljs-selector-tag">gradle</span>
├── <span class="hljs-selector-tag">gradle</span><span class="hljs-selector-class">.properties</span>
├── <span class="hljs-selector-tag">gradlew</span>
├── <span class="hljs-selector-tag">gradlew</span><span class="hljs-selector-class">.bat</span>
├── <span class="hljs-selector-tag">local</span><span class="hljs-selector-class">.properties</span>
└── <span class="hljs-selector-tag">settings</span><span class="hljs-selector-class">.gradle</span>
</code></pre><p>Now we will create the 'commonlib' Git repository and put the 'commonlib' subdirectory there. All other files (except those that shouldn't be in Git at all) will go to the 'app1' Git repository. After that, the 'commonlib' repository will be inserted into the 'app1' Git repository.</p>
<p>Create the 'commonlib' Git repository using the Atlassian Bitbucket Server/Data Center interface.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/b39d473a3feac416eb3a840130a422d1f2e35188.png" alt="10-create-commonlib-repository|658x500" /> 
Copy the Git URL of this newly created repository, e.g.</p>
<pre><code><span class="hljs-attribute">http</span>:<span class="hljs-comment">//example.org/scm/android/commonlib.git</span>
</code></pre><p>The simplest way to push 'commonlib' content (except the 'build' directory) to this Git repository is the following:</p>
<pre><code>cd commonlib/
git <span class="hljs-keyword">init</span> .
$ git <span class="hljs-keyword">add</span> src
git <span class="hljs-keyword">add</span> build.gradle 
git <span class="hljs-keyword">add</span> consumer-rules.pro
git <span class="hljs-keyword">add</span> proguard-rules.pro
git commit -m <span class="hljs-string">"Initial."</span>
git push http:<span class="hljs-comment">//example.org/scm/android/commonlib.git master</span>
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/80bb96d5646a81750872fa8dd51787b745da24a1.png" alt="11-commonlib-repository-toc|541x500" /> 
Now create the 'app1' Git repository with Atlassian Bitbucket Server/Data Center.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/118d325f7a92ab6fe86554dfd45a9e9a398b0e7e.png" alt="12-create-app1-repository|657x500" /> 
Suppose its URL is</p>
<pre><code><span class="hljs-attribute">http</span>:<span class="hljs-comment">//example.org/scm/android/app1.git</span>
</code></pre><p>Put everything except <code>commonlib</code>, <code>app/build</code>, and <code>local.properties</code> to this repository.</p>
<pre><code>cd ..
git <span class="hljs-keyword">init</span> .
git <span class="hljs-keyword">add</span> app/build.gradle 
git <span class="hljs-keyword">add</span> app/libs
git <span class="hljs-keyword">add</span> app/proguard-rules.pro 
git <span class="hljs-keyword">add</span> app/src
git <span class="hljs-keyword">add</span> gradle
git <span class="hljs-keyword">add</span> gradlew
git <span class="hljs-keyword">add</span> gradlew.bat 
git <span class="hljs-keyword">add</span> gradle.properties 
git <span class="hljs-keyword">add</span> build.gradle
git <span class="hljs-keyword">add</span> settings.gradle 
git commit -m <span class="hljs-string">"Initial."</span>
git push http:<span class="hljs-comment">//example.org/scm/android/app1.git master</span>
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/55bb88f8caf2e75bdfaacd42cc65e469041cf3a0.png" alt="13-app1-repository-toc|512x500" /> </p>
<p><strong>Step 4.</strong> Insert 'commonlib' into the 'app1' repository.</p>
<p>We will be using  <a target="_blank" href="https://tmatesoft.com/x-modules">Git X-Modules</a>. It inserts one repository into another on the server's side as Git trees. So, for the Git client, the module will be just a part of a regular Git tree.</p>
<p>If you don't have the Git X-Modules app installed, go to <code>Administration | Find new apps | Search the Marketplace</code> and type "X-Modules" in the Bitbucket Server/Data Center UI to install this app.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7c874cca26baf6d433d63b89b39b0eedf20c36d1.png" alt="07-install-x-modules-app|690x203" /> 
Now go to the 'app1' Git repository page. Click the "Git X-Modules" button on the sidebar. 
<img src="https://support.tmatesoft.com/uploads/default/original/1X/30c200fa3dd3234e27f1782f55b570513b4dbce7.png" alt="09-x-modules-button|690x181" /> 
Now click "Add Module" to add 'commonlib' to the project.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/46e007817169e53196cb4ad23826dfded5c33371.png" alt="11-x-modules-add-first-module|690x275" /> 
Choose the 'commonlib' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c2e05d77123f1eb1c06e4ffd6a8a30740b9bf7dd.png" alt="17-x-modules-choose-commonlib|606x500" /> 
And the 'master' branch.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/e5fed2f107de9cffd76b2531aa75afdb586d0a17_2_1035x613.png" alt="18-x-modules-choose-master-branch|690x409" /> 
Make sure "This Repository Path" is 'commonlib'. It's the path in 'app1' where the 'commonlib' repository will be inserted.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/009ee3391e22e05f9221b6033836d635b23cbb2a_2_1035x639.png" alt="19-x-modules-apply-changes|690x426" /> 
Click "Add Module" and apply changes. Now 'app1' Git repository has 'commonlib' directory with 'commonlib' Git repository inserted there.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/e5b6dce34972511f5e2c2b03aa9b90af3c65d5aa.png" alt="19-app1-repository-toc|398x500" /> 
Now any team member can clone the 'app1' Git repository, create <code>local.properties</code>, and build it with <code>./gradlew build</code>. Alternatively one could add <code>local.properties.example</code> into the repository to make it simpler to start working with the project.</p>
<p><strong>Step 5.</strong> Create the 'app2' repository.</p>
<p>In the same way as with 'App1', create the 'App2' application project.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/0d96d9bab41986cf1794a5369d6bea03adb5ebdd.png" alt="01-choose-template|656x500" /> 
<img src="https://support.tmatesoft.com/uploads/default/original/1X/9ffce43ab548e1eed3d1b738f4fe0924da12e4ea.png" alt="21-choose-app2-name|656x500" /> 
Create the 'app2' Git repository using Bitbucket Server/Data Center UI.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c576b7634cf01f0a55e97e065078bc86e397166a.png" alt="22-create-app2-repository|658x500" /> 
Put 'App1' project content to the 'app1' Git repository.</p>
<pre><code>cd app2
git <span class="hljs-keyword">init</span> .
git <span class="hljs-keyword">add</span> app/
git <span class="hljs-keyword">add</span> build.gradle
git <span class="hljs-keyword">add</span> gradle/
git <span class="hljs-keyword">add</span> gradle. properties
git <span class="hljs-keyword">add</span> gradlew
git <span class="hljs-keyword">add</span> gradlew.bat
git <span class="hljs-keyword">add</span> settings.gradle
git <span class="hljs-keyword">add</span> .gitignore
git commit -m <span class="hljs-string">"Initial."</span>
git remote <span class="hljs-keyword">set</span>-url origin http:<span class="hljs-comment">//example.org/scm/android/app1.git</span>
git push origin master
</code></pre><p><code>http://example.org/scm/android/app2.git</code> is the Git URL of the 'app2' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/d12125ab2423deb4f031b5629b26313e2c550154.png" alt="22-app2-repository-toc|426x500" /> </p>
<p><strong>Step 6.</strong> Insert 'commonlib' into the 'app2' repository using Git X-Modules.</p>
<p>In a similar way, click the Git X-Modules button on the 'app2' repository page, add the 'commonlib' repository as X-Module to the 'commonlib' directory ("This repository path").
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7b3c15b0d1a6b348e9e91d04c8f42925aebe4e7e.png" alt="23-x-modules-app2-preview|690x462" /> 
Apply the changes.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/6e2cdfb0af45a114f20de56c5f6409367aab402b_2_1035x648.png" alt="23-x-modules-apply-changes|690x432" /> 
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c09d41186ff748a2254bad6caed5c5b3c93d0521.png" alt="23-app2-repository-toc|411x500" /> </p>
<p><strong>Step 7.</strong> Add dependency for 'app2' on 'commonlib'.</p>
<p>To fetch the changes, run from 'app2':</p>
<pre><code>git remote <span class="hljs-keyword">set</span>-<span class="hljs-keyword">url</span> origin <span class="hljs-keyword">http</span>://example.org/scm/android/app2.git
git pull <span class="hljs-comment">--rebase</span>
</code></pre><p>Change `settings.gradle' to include ':commonlib' subdirectory.</p>
<pre><code><span class="hljs-keyword">include</span> <span class="hljs-string">':app'</span>
<span class="hljs-keyword">include</span> <span class="hljs-string">':commonlib'</span>
rootProject.name = "App2"
</code></pre><p>Change <code>app/build.gradle</code> to add:</p>
<pre><code>   <span class="hljs-selector-tag">implementation</span> <span class="hljs-selector-tag">project</span>(<span class="hljs-attribute">path</span>: <span class="hljs-string">':commonlib'</span>)
</code></pre><p>to the dependencies list.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/8a88f57beb46659b1a61d429f16e13e71789cafd.png" alt="24-diff|690x460" /> 
Commit and push the changes:</p>
<pre><code>git <span class="hljs-keyword">add</span> app/build.gradle
git <span class="hljs-keyword">add</span> settings.gradle
git <span class="hljs-keyword">commit</span> -m "Add dependency on 'commonlib'."
git push origin master
</code></pre><p><strong>Step 8.</strong> Test using 'commonlib' in 'app2'.</p>
<p>Change MainActivity in 'app2' to call <code>CommonLib.helloFromCommonLib()</code>.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c6564a575e7326d8753eb374b47c52e0355c7fb0.png" alt="25-change-app2|690x313" /> 
Run the result in the emulator to see how it works.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/cefadd9752723e7be1e6740cc2599c489729af9b.png" alt="26-changed-app2-emulator|233x500" /> 
Commit and push the changes.</p>
<p><strong>Step 9.</strong> Test changing 'commonlib'.</p>
<p>Change 'commonlib' in the 'app2' repository,
<img src="https://support.tmatesoft.com/uploads/default/original/1X/79d655c3c55b57d3b3d854b1771075314727f086.png" alt="27-update-commonlib|690x395" /> 
push the changes, get the changes in 'app1' and make sure it's updated.</p>
<pre><code>git <span class="hljs-keyword">commit</span> -a -m <span class="hljs-string">"'commonlib' updated"</span>
git push origin <span class="hljs-keyword">master</span>
cd ..
rm -rf app1
git <span class="hljs-keyword">clone</span> <span class="hljs-keyword">http</span>://example.org/scm/android/app1.git app1/
</code></pre><p>Run 'app1' to make sure 'commonlib' is automatically updated.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/6cb7e9db97d462526e5f35b8a9b16e4d6a6bbfd9.png" alt="28-updated-commonlib-app1-emulator|233x500" /> 
As you may see, the 'commonlib' is now automatically synchronized between both apps.</p>
]]></content:encoded></item><item><title><![CDATA[Multi-module Gradle configuration with Git X-Modules]]></title><description><![CDATA[How to build a multi-component app in Gradle without uploading each component to Maven each time it's updated.
Developing an application and a few libraries in parallel could be quite painful.
On one hand, it makes sense to create a separate Git repo...]]></description><link>https://blog.tmatesoft.com/multi-module-gradle-configuration-with-git-x-modules</link><guid isPermaLink="true">https://blog.tmatesoft.com/multi-module-gradle-configuration-with-git-x-modules</guid><category><![CDATA[gradle]]></category><category><![CDATA[Git]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Sun, 22 Nov 2020 19:54:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606074444411/fhZQHgoyT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><em>How to build a multi-component app in Gradle without uploading each component to Maven each time it's updated.</em></strong></p>
<p>Developing an application and a few libraries in parallel could be quite painful.</p>
<p>On one hand, it makes sense to create a separate Git repository (and a separate Gradle project) for each library and for the app itself. Then the libraries and the app would be connected via Gradle dependencies. So if a bug in the app is caused by a bug in one of the libraries, to fix it one has to
  1) make a change in the library;
  2) build and upload the library artifacts to the Maven/Ivy repository;
  3) download the new versions of the library artifacts from the app project;
  4) build the app and ...
  5) ... check if the change actually fixes the app, if not, go to (1)</p>
<p>This cycle is annoying and slow, especially if the building process involves running all tests on the CI. 
<img src="https://support.tmatesoft.com/uploads/default/original/1X/2f5fb2b5e88d1dd76c29525d47898e5a38d8eae6.png" alt="01-problem|504x418" /> 
On the other hand, there's another approach: put all libraries and apps into the same Git repository (so-called "monorepo"). It would work for some projects but in general it's <a target="_blank" href="https://medium.com/@alexey.soshin/monorepo-is-a-bad-idea-5e587e848a07">not a good idea</a>. </p>
<p>One could put the common library into a Git submodule to the app repository, but ...
<img src="https://support.tmatesoft.com/uploads/default/original/1X/fdaac8817398d055b1c961b58ecf294582092358.png" alt="02-submodules-tweets|690x250" /> </p>
<p>In this post, I will describe how to set up a multi-component (libraries + an app) Gradle-based project that uses <a target="_blank" href="https://tmatesoft.com/x-modules">Git X-Modules</a> to glue them together. This solution has the best from both worlds: every component resides in its own repository and can be built separately. But also the app can be built together with the libraries thus avoiding the "painful cycle".</p>
<p><strong>Infrastructure</strong>
For the purpose of this project, I will use Atlassian <a target="_blank" href="https://www.atlassian.com/software/bitbucket/download">Bitbucket Server</a>/<a target="_blank" href="https://www.atlassian.com/software/bitbucket/enterprise/data-center">Data Center</a> as the Git server. It's also convenient because there's a special <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696">Git X-Modules App</a> for it. If you are on any other Git Server,  the configuration and the steps would be similar,  although the UI will look different, and you will have to use Git X-Modules as a <a target="_blank" href="https://gitmodules.com">command-line tool</a>.  </p>
<p>I will also use the latest version of Gradle to date, which is 6.7.1.</p>
<p>As an example, I will create a simple Java application that uses a simple library. The application will be named <strong>'app'</strong> and the library will be named <strong>'lib'</strong>.</p>
<p><strong>The approach.</strong>
To create a multi-component Gradle-based project I will use the <a target="_blank" href="https://docs.gradle.org/current/userguide/composite_builds.html">"Composite builds"</a> feature of Gradle.</p>
<p>I will create 3 Git repositories: one for the 'app', one for the 'lib', and the third one (<strong>'parent'</strong>) to unite both 'app' and 'lib' under one roof.</p>
<p>Each of the repositories can be cloned and built with Gradle independently of all others.</p>
<p><strong>Step 1.</strong> Create a Git repository for 'lib'.</p>
<p>Create a 'lib' repository using Atlassian Bitbucket Server/Data Center UI.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/82ab2497cc0b08ef93b7673c0fcdea65e258a51e_2_982x750.png" alt="04-create-lib-repository|655x500" /> 
Clone this empty Git repository (I suppose the Bitbucket Server/Data Center is run on example.org domain):</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/gradle-example/lib.git lib/</span>
cd lib/
</code></pre><p>Create <code>src/main/java/library/Library.java</code> file with the following content:</p>
<pre><code>package library;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Library</span> {

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">printGreetings</span>(<span class="hljs-params"></span>)</span> {
        System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Hello from the library!"</span>);
    }
}
</code></pre><p>Create <code>build.gradle</code> with this content:</p>
<pre><code>apply plugin: <span class="hljs-string">'java'</span>

<span class="hljs-keyword">group</span> "lib"
<span class="hljs-keyword">version</span> "1.0"
</code></pre><p>The project is quite minimalistic, its structure is:</p>
<pre><code>├── <span class="hljs-selector-tag">build</span><span class="hljs-selector-class">.gradle</span>
└── <span class="hljs-selector-tag">src</span>
    └── <span class="hljs-selector-tag">main</span>
        └── <span class="hljs-selector-tag">java</span>
            └── <span class="hljs-selector-tag">library</span>
                └── <span class="hljs-selector-tag">Library</span><span class="hljs-selector-class">.java</span>
</code></pre><p>Make sure the 'lib' project can be built separately:</p>
<pre><code><span class="hljs-attribute">gradle</span> build

<span class="hljs-attribute">BUILD</span> SUCCESSFUL in <span class="hljs-number">925</span>ms
<span class="hljs-attribute">2</span> actionable tasks: <span class="hljs-number">2</span> executed
</code></pre><p>Commit and push the changes:</p>
<pre><code>git <span class="hljs-keyword">add</span> src/main/java/library/Library.java
git <span class="hljs-keyword">add</span> build.gradle
echo "/build/" &gt; .gitignore
git <span class="hljs-keyword">add</span> .gitignore
git <span class="hljs-keyword">commit</span> -m "Initial."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/2e64bd999ad2d87370fceddf28b7bebe989fd2a7.png" alt="05-lib-repository-toc|612x500" /> </p>
<p><strong>Step 2.</strong> Create a Git repository for 'app'.</p>
<p>Create an 'app' repository using Atlassian Bitbucket Server/Data Center UI.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7bd4522dcd8db2e24a884d94b91cf3d9109d531f.png" alt="06-create-app-repository|683x500" /> 
Clone this empty repository:</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/gradle-example/app.git app/</span>
cd app/
</code></pre><p>Create <code>src/main/java/app/App.java</code> file:</p>
<pre><code><span class="hljs-keyword">package</span> app;

<span class="hljs-keyword">import</span> library.Library;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Library.printGreetings();
        System.out.println(<span class="hljs-string">"Hello World!"</span>);
    }
}
</code></pre><p>It invokes the library method. Also, create <code>build.gradle</code>:</p>
<pre><code><span class="hljs-attribute">apply</span> plugin: <span class="hljs-string">'java'</span>
apply plugin: <span class="hljs-string">'application'</span>

mainClassName=<span class="hljs-string">'app.App'</span>

dependencies {
    <span class="hljs-attribute">implementation</span> <span class="hljs-string">'lib:lib:1.0'</span>
}
</code></pre><p>Because of the dependency, its compilation will fail unless the artifact of the 'lib' project is uploaded to the Maven repository:</p>
<pre><code><span class="hljs-attribute">gradle</span> build
</code></pre><pre><code>&gt; Task :compileJava FAILED

FAILURE: Build failed <span class="hljs-keyword">with</span> an exception.

* What went wrong:
Execution failed <span class="hljs-keyword">for</span> task <span class="hljs-string">':compileJava'</span>.
&gt; Could not resolve all files <span class="hljs-keyword">for</span> configuration <span class="hljs-string">':compileClasspath'</span>.
   &gt; Cannot resolve <span class="hljs-keyword">external</span> dependency lib:lib:<span class="hljs-number">1.0</span> because no repositories are defined.
     Required by:
         project :

* Try:
Run <span class="hljs-keyword">with</span> --stacktrace option to <span class="hljs-keyword">get</span> the stack trace. Run <span class="hljs-keyword">with</span> --info or --debug option to <span class="hljs-keyword">get</span> more log output. Run <span class="hljs-keyword">with</span> --scan to <span class="hljs-keyword">get</span> full insights.

* Get more help at https:<span class="hljs-comment">//help.gradle.org</span>

BUILD FAILED <span class="hljs-keyword">in</span> <span class="hljs-number">736</span>ms
<span class="hljs-number">1</span> actionable task: <span class="hljs-number">1</span> executed
</code></pre><p>But once the artifact is uploaded to the Maven/Ivy repository, the compilation will succeed.</p>
<p>Commit and push the changes:</p>
<pre><code>git <span class="hljs-keyword">add</span> src/main/java/app/App.java
git <span class="hljs-keyword">add</span> build.gradle
echo "/build/" &gt; .gitignore
git <span class="hljs-keyword">add</span> .gitignore
git <span class="hljs-keyword">commit</span> -m "Initial."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/5bfa9295a07a55dd4a26f096f02774c872c942e2.png" alt="07-app-repository-toc|603x500" /> </p>
<p><strong>Step 3.</strong> Make sure that the Git X-Modules app is installed.</p>
<p>From Bitbucket Server/Data Center UI go to <code>Administration | Find new apps | Search the</code> <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696/git-x-modules-for-bitbucket-server?hosting=server&amp;tab=overview">Marketplace</a> and type "X-Modules". Install the app if it's not installed.<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/7c874cca26baf6d433d63b89b39b0eedf20c36d1_2_1035x304.png" alt="07-install-x-modules-app|690x203" /> </p>
<p><strong>Step 4.</strong> Create a Git repository for 'parent'.
I will create a 'parent' repository that would contain 'lib' and 'app' as X-Modules (from the Git perspective they will be regular Git directories). And then I will add additional Gradle configuration files to build the whole project from sources, skipping Maven/Ivy.</p>
<p>Create a 'parent' repository using Atlassian Bitbucket Server/Data Center UI.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7e4c79151dda209e20309767454ea79a25923ff0.png" alt="09-create-parent-repository|660x500" />
When the Git X-Modules app is installed there's an "X-Modules" button on the 'parent' repository page. Click it.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/30c200fa3dd3234e27f1782f55b570513b4dbce7.png" alt="09-x-modules-button|690x181" /> </p>
<p>As there're no branches in the repository, click "Create Default Branch" to create 'master'.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/bb5a571da2a319cbfaed6af9471d17934181f539_2_1035x427.png" alt="10-x-modules-create-default-branch|690x285" /> </p>
<p>Now click "Add Module" to add 'lib' to the project.
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/46e007817169e53196cb4ad23826dfded5c33371_2_1035x412.png" alt="11-x-modules-add-first-module|690x275" /> 
Choose the 'lib' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/93b87b4c0c46b72c18998fd04479325b608976a8.png" alt="13-x-modules-choose-lib-repository|528x500" /> 
And the 'master' branch.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/58301d916815f173fafd7a42c76d4fabd080594d.png" alt="14-x-modules-choose-master-branch|580x305" /> 
Make sure "This Repository Path" is "lib". It's the path where the 'lib' repository will be inserted.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/8af68ba1b9e8c1711a8a538a32e8d23223a14d1c.png" alt="16-x-modules-choose-lib-module-path|690x40" /> 
Click "Add Module".
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/ed92f73773443c55c4c29b85db94ace513ef8d09_2_1035x621.png" alt="15-x-modules-add-another-module|690x414" /> 
Without applying the changes click "Add Module" again.</p>
<p>Choose the 'app' repository and 'master' branch in it.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/1b34a9f0272cad41f1730ff3b1098b844e83a179.png" alt="17-x-modules-choose-app-repository|517x500" /> 
Now make sure "This Repository Path" is "app".
<img src="https://support.tmatesoft.com/uploads/default/original/1X/e2a9bf19139c5d69ce3610fd98bb3cac8ea16d5b.png" alt="18-x-modules-app-module-path|690x42" /> 
Click "Add Module".
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/13db950c8e2825d1b5abcf6a42967385667b9fc2_2_1035x553.png" alt="19-x-modules-preview|690x369" /> 
Apply the changes.</p>
<p>Now the 'parent' repository contains 'lib' and 'app' subdirectories with the content of 'lib' and 'app' repositories correspondingly.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/97c8895512708a417fdad12085621cf68a151016.png" alt="20-parent-repository-toc|684x500" /> 
Clone  the 'parent' repository:</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/gradle-example/parent.git parent/</span>
cd <span class="hljs-built_in">parent</span>/
</code></pre><pre><code>├── <span class="hljs-selector-tag">app</span>
│   ├── <span class="hljs-selector-tag">build</span><span class="hljs-selector-class">.gradle</span>
│   └── <span class="hljs-selector-tag">src</span>
│       └── <span class="hljs-selector-tag">main</span>
│           └── <span class="hljs-selector-tag">java</span>
│               └── <span class="hljs-selector-tag">app</span>
│                   └── <span class="hljs-selector-tag">App</span><span class="hljs-selector-class">.java</span>
└── <span class="hljs-selector-tag">lib</span>
    ├── <span class="hljs-selector-tag">build</span><span class="hljs-selector-class">.gradle</span>
    └── <span class="hljs-selector-tag">src</span>
        └── <span class="hljs-selector-tag">main</span>
            └── <span class="hljs-selector-tag">java</span>
                └── <span class="hljs-selector-tag">library</span>
                    └── <span class="hljs-selector-tag">Library</span><span class="hljs-selector-class">.java</span>
</code></pre><p>Now create the <code>settings.gradle</code> file with the following content:</p>
<pre><code>rootProject.name = <span class="hljs-string">'parent'</span>

includeBuild <span class="hljs-string">'lib'</span>
includeBuild <span class="hljs-string">'app'</span>
</code></pre><p>It will tell Gradle to build 'lib' from sources instead of getting the corresponding artefact from Maven. Finally, create <code>build.gradle</code>:</p>
<pre><code><span class="hljs-selector-tag">tasks</span><span class="hljs-selector-class">.register</span>(<span class="hljs-string">'run'</span>) {
    <span class="hljs-selector-tag">dependsOn</span> <span class="hljs-selector-tag">gradle</span><span class="hljs-selector-class">.includedBuild</span>(<span class="hljs-string">'app'</span>)<span class="hljs-selector-class">.task</span>(<span class="hljs-string">':run'</span>)
}
</code></pre><p>This will create a "run" task for the parent project and delegate it to the 'app' project.</p>
<p>Now the parent project can be built from sources bypassing Maven:</p>
<pre><code><span class="hljs-attribute">gradle</span> build
gradle run
</code></pre><pre><code>&gt; <span class="hljs-selector-tag">Task</span> <span class="hljs-selector-pseudo">:app</span><span class="hljs-selector-pseudo">:run</span>
<span class="hljs-selector-tag">Hello</span> <span class="hljs-selector-tag">from</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">library</span>!
<span class="hljs-selector-tag">Hello</span> <span class="hljs-selector-tag">World</span>!

<span class="hljs-selector-tag">BUILD</span> <span class="hljs-selector-tag">SUCCESSFUL</span> <span class="hljs-selector-tag">in</span> <span class="hljs-selector-tag">936ms</span>
</code></pre><p>Add and commit the changes:</p>
<pre><code>git <span class="hljs-keyword">add</span> build.gradle
git <span class="hljs-keyword">add</span> settings.gradle
git <span class="hljs-keyword">commit</span> -m "Gradle configuration files added."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/2ad1db15c9fd64f6988efb27117b0fae41c7c627.png" alt="21-structure|514x364" /> 
Now the parent repository contains both 'lib' and 'app' modules and can be built from sources independently.  Any change to "lib/" and "app/" subdirectories of the parent will be automatically synchronized with 'lib' and 'app' repositories by the Git X-Modules app.</p>
<p>The 'lib' repository can be also used by another app. Just create another 'parent' repository containing the 'lib' repository and the other app's repository as X-Modules.</p>
]]></content:encoded></item><item><title><![CDATA[Multi-component CMake-based project with Git X-Modules]]></title><description><![CDATA[How to build a multi-component project without installing all libraries.
At the time of writing this, there's no default solution for dependency management in the C world. There're several competing tools like Basel, Meson, conan, etc but none of the...]]></description><link>https://blog.tmatesoft.com/multi-component-cmake-based-project-with-git-x-modules</link><guid isPermaLink="true">https://blog.tmatesoft.com/multi-component-cmake-based-project-with-git-x-modules</guid><category><![CDATA[C++]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Sun, 22 Nov 2020 18:47:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606071402790/R7oXfRDm0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><em>How to build a multi-component project without installing all libraries.</em></strong></p>
<p>At the time of writing this, there's no default solution for dependency management in the C world. There're several competing tools like <a target="_blank" href="https://bazel.build/">Basel</a>, <a target="_blank" href="https://mesonbuild.com/">Meson</a>, <a target="_blank" href="https://conan.io/">conan</a>, etc but none of them is recognized as standard de facto like Maven in Java world.</p>
<p>In this post, we will describe a <a target="_blank" href="https://cmake.org/">CMake</a>-based setup of a project consisting of several modules, which:</p>
<ul>
<li>can be built and installed independently;</li>
<li>can be build together, so all the app and library sources can be edited, debugged, and committed simultaneously.</li>
</ul>
<p>This setup resolves the pain of "fix and build the library" -&gt; "install the library" -&gt; "build the app with the library" -&gt; "test the fix from the app" cycle.</p>
<p>As an example, we will create an app that computes SHA-1 checksum of its argument. To compute SHA-1 checksum we will use an external <a target="_blank" href="https://github.com/clibs/sha1">library</a> by Steve Reid.</p>
<p><strong>Project structure</strong>.
Currently, the SHA-1 library in question has only a Makefile to build the project. We want to add a CMake-related build file (CMakeLists.txt) to this library. As we don't have write access to this repository we will fork this project to our Git server. For that, we will use Atlassian <a target="_blank" href="https://www.atlassian.com/software/bitbucket/download">Bitbucket Server</a>/<a target="_blank" href="https://www.atlassian.com/enterprise/data-center/bitbucket">Data Center</a> as one of the most popular self-hosting Git solutions.  It's also convenient because there's a special <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696">Git X-Modules App</a> for it. If you are on any other Git Server, you may still use Git X-Modules as a <a target="_blank" href="https://gitmodules.com">command-line tool</a>. </p>
<p>The SHA-1 library component  will be called <strong>'sha-1'</strong>.  Then we will create a <strong>'checksum'</strong> component that uses 'sha-1' library and produces an executable file. One should be able to build this component independently assuming 'sha-1' library is installed (into <code>/usr/lib and /usr/include</code> or any other OS-specific directory for libraries).</p>
<p>Finally, we will create a <strong>'checksum-project'</strong> that includes both 'sha-1' and 'checksum' components and can be built <em>without</em> having to install 'sha-1' library to the system. This will allow us to open, edit, and build all components in an IDE.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/8abc6f21ff0c700961723a2b2cb4825b6cc38aef.png" alt="22-structure|434x264" /> </p>
<p>From the Git perspective, one can use Git submodules for that. But this solution causes a lot of problems. For example, the Projectile package of Emacs doesn't currently work well with submodules. We will use a better solution: <a target="_blank" href="https://tmatesoft.com/x-modules.html">Git X-Modules</a>.</p>
<p><strong>Step 1.</strong>
Create 'sha-1' repository with Atlassian Bitbucket Server/Data Center interface.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/06f27e5b05a1c53f7e95984f5b0cccc0b9ad1994.png" alt="01-create-sha-1-repository|449x500" /> </p>
<p>We want now to fork https://github.com/clibs/sha1 project there. To do that, clone the sha1 project from GitHub and push it to our self-hosted Bitbucket Server repository (we assume it's running on example.org domain):</p>
<pre><code>git clone https://github.com/clibs/sha1.git sha-1
cd sha-1
git remote <span class="hljs-keyword">set</span>-<span class="hljs-keyword">url</span> origin <span class="hljs-keyword">http</span>://example.org/scm/<span class="hljs-keyword">checksum</span>/<span class="hljs-keyword">sha</span><span class="hljs-number">-1.</span>git
git push origin <span class="hljs-keyword">master</span>
</code></pre><p><strong>Step 2</strong>. Add our custom CMakeLists.txt to 'sha-1' project with the following content:</p>
<pre><code><span class="hljs-attribute">cmake_minimum_required</span>(VERSION <span class="hljs-number">3</span>.<span class="hljs-number">8</span> FATAL_ERROR)
<span class="hljs-attribute">project</span>(sha<span class="hljs-number">1</span>)

<span class="hljs-attribute">add_library</span>(sha<span class="hljs-number">1</span> sha<span class="hljs-number">1</span>.c)
<span class="hljs-attribute">target_include_directories</span>(sha<span class="hljs-number">1</span> PUBLIC .)
<span class="hljs-attribute">set_target_properties</span>(sha<span class="hljs-number">1</span> PROPERTIES PUBLIC_HEADER <span class="hljs-string">"sha1.h"</span>)
<span class="hljs-attribute">install</span>(TARGETS sha<span class="hljs-number">1</span> LIBRARY DESTINATION lib ARCHIVE DESTINATION lib PUBLIC_HEADER DESTINATION include)
</code></pre><p>The <code>install()</code> command tells CMake to install the library to /usr/local/lib and the header to /usr/local/include, so it can be easily included by the app.</p>
<p>The <code>target_include_directories()</code> call is not necessary to build this project but will be useful later for the multi-module project structure.</p>
<p>Commit and push the change:</p>
<pre><code>git <span class="hljs-keyword">add</span> CMakeLists.txt
git <span class="hljs-keyword">commit</span> -m "CMakeLists.txt added."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/ca5cf1e946433475e190a79684b2b9a0e41bfb71.png" alt="02-sha-1-repository-toc|615x500" /> 
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/433ea818ae94e83750dac432f6b428d264bdae40_2_1035x270.png" alt="03-sha-1-repository-history|690x180" /> </p>
<p>Make sure CMake can build the project. We can use these old-fashioned commands:</p>
<pre><code>mkdir build
<span class="hljs-built_in">cd</span> build
cmake ..
make
</code></pre><p>To install the library into the system one can use "sudo make install" command but later we will describe how to create a multi-component structure that doesn't need installation. So we <strong>do not</strong> install the library.</p>
<p><strong>Step 3.</strong> Create a Git repository for our app.</p>
<p>Create a 'checksum' repository using Atlassian Bitbucket Server/Data Center interface.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/5144a08fedc17f593077a4fcf2fa6b6088ad2ccb.png" alt="04-create-checksum-repository|446x500" /> </p>
<p>Clone and "cd" into the repository:</p>
<pre><code>git clone http://example.org/scm/<span class="hljs-keyword">checksum</span>/checksum.git <span class="hljs-keyword">checksum</span>
cd <span class="hljs-keyword">checksum</span>
</code></pre><p>Create the <code>main.c</code> file with the following content:</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;sha1.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string.h&gt;</span></span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SHA1_SIZE_IN_BYTES 20</span>

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">print_usage</span><span class="hljs-params">(<span class="hljs-keyword">char</span>* command)</span> </span>{
  <span class="hljs-built_in">fprintf</span>(<span class="hljs-built_in">stderr</span>, <span class="hljs-string">"Usage: %s STRING\n"</span>, command);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">print_hex</span><span class="hljs-params">(<span class="hljs-keyword">char</span>* buffer, <span class="hljs-keyword">int</span> buffer_size)</span> </span>{
  <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; buffer_size; i++) {
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%02x"</span>, buffer[i] &amp; <span class="hljs-number">0xff</span>);
  }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-keyword">int</span> argc, <span class="hljs-keyword">char</span>** argv)</span> </span>{
  <span class="hljs-keyword">if</span> (argc != <span class="hljs-number">2</span>) {
    print_usage(argv[<span class="hljs-number">0</span>]);
    <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
  }
  <span class="hljs-keyword">char</span> sha1[SHA1_SIZE_IN_BYTES + <span class="hljs-number">1</span>];
  <span class="hljs-keyword">char</span>* str = argv[<span class="hljs-number">1</span>];

  SHA1(sha1, str, <span class="hljs-built_in">strlen</span>(str));
  sha1[SHA1_SIZE_IN_BYTES] = <span class="hljs-string">'\0'</span>;

  print_hex(sha1, SHA1_SIZE_IN_BYTES);
  <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);

  <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre><p>Then create CMakeLists.txt file with this content:</p>
<pre><code>cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(<span class="hljs-keyword">checksum</span>)

add_executable(<span class="hljs-keyword">checksum</span> main.c)

target_include_directories(<span class="hljs-keyword">checksum</span> <span class="hljs-keyword">PRIVATE</span> <span class="hljs-keyword">sha1</span>)
target_link_libraries(<span class="hljs-keyword">checksum</span> <span class="hljs-keyword">PRIVATE</span> <span class="hljs-keyword">sha1</span>)
</code></pre><p>The <code>main.c</code> is simple: it just calls <code>SHA1()</code> function from 'sha-1' library and CMakeLists.txt adds a dependency on 'sha-1' library. Commit and push the change:</p>
<pre><code>git <span class="hljs-keyword">add</span> main.c CMakeLists.txt
git <span class="hljs-keyword">commit</span> -m "Initial commit."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/8bf90573f5185952767358e65e94be2dc2d257ab.png" alt="05-checksum-repository-toc|690x358" /> 
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7ce79b172fbfec53a55a30a5c4d2400f5c8536b0.png" alt="06-checksum-repository-history|653x316" /> </p>
<p>If 'sha-1' is installed on the system with "sudo make install" command of "Step 2", our <code>checksum</code> app can be build using CMake. But if 'sha-1' is not installed, CMake build command will fail because it can't find the header file for the library:</p>
<pre><code>mkdir build
<span class="hljs-built_in">cd</span> build
cmake ..
make
</code></pre><pre><code>Scanning dependencies of target <span class="hljs-keyword">checksum</span>
[ <span class="hljs-number">50</span>%] Building C <span class="hljs-keyword">object</span> CMakeFiles/checksum.dir/main.c.o
/home/dmit10/<span class="hljs-keyword">work</span>/blog/<span class="hljs-number">02</span>-cmake-post/<span class="hljs-keyword">checksum</span>/main.c:<span class="hljs-number">1</span>:<span class="hljs-number">10</span>: fatal <span class="hljs-keyword">error</span>: sha1.h: <span class="hljs-keyword">No</span> such <span class="hljs-keyword">file</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">directory</span>
 <span class="hljs-comment">#include &lt;sha1.h&gt;</span>
          ^~~~~~~~
compilation terminated.
make[<span class="hljs-number">2</span>]: *** [CMakeFiles/checksum.dir/build.make:<span class="hljs-number">63</span>: CMakeFiles/checksum.dir/main.c.o] <span class="hljs-keyword">Error</span> <span class="hljs-number">1</span>
make[<span class="hljs-number">1</span>]: *** [CMakeFiles/Makefile2:<span class="hljs-number">73</span>: CMakeFiles/checksum.dir/<span class="hljs-keyword">all</span>] <span class="hljs-keyword">Error</span> <span class="hljs-number">2</span>
make: *** [Makefile:<span class="hljs-number">84</span>: <span class="hljs-keyword">all</span>] <span class="hljs-keyword">Error</span> <span class="hljs-number">2</span>
</code></pre><p><strong>Step 4.</strong> Multi-module structure.
Now we want to create a multi-module repository 'checksum-project' with the following structure:</p>
<pre><code>CMakeLists.txt
checksum/    &lt;-- <span class="hljs-string">'checksum'</span> <span class="hljs-built_in">module</span> insertion point
libs<span class="hljs-regexp">/sha-1/</span>  &lt;-- <span class="hljs-string">'sha-1'</span> library insertion point
</code></pre><p>If you don't have the <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696/git-x-modules-for-bitbucket-server?ho">Git X-Modules app</a> for Bitbucket installed, install it now. To do so,  go to <code>Administration | Find new apps | Search the Marketplace</code> and type "X-Modules".
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7c874cca26baf6d433d63b89b39b0eedf20c36d1.png" alt="07-install-x-modules-app|690x203" /> </p>
<p>Now create a new Git repository and name it 'checksum-project'.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/46de641b8f1043fdf41c978d73f8e44b9a3d89ba.png" alt="08-create-checksum-project-repository|449x500" /> </p>
<p>Once the Git X-Modules app is installed, there will be an X-Modules button on the Git repository page. Click it.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/30c200fa3dd3234e27f1782f55b570513b4dbce7.png" alt="09-x-modules-button|690x181" /> </p>
<p>As the repository is empty, click "Create Default Branch".
<img src="https://support.tmatesoft.com/uploads/default/original/1X/bb5a571da2a319cbfaed6af9471d17934181f539.png" alt="10-x-modules-create-default-branch|690x285" /> </p>
<p>Then click "Add Module" to add the 'sha-1' library.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/46e007817169e53196cb4ad23826dfded5c33371.png" alt="11-x-modules-add-first-module|690x275" /> </p>
<p>Choose the 'sha-1' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/aaee3fd71e91a49c578084f44a69b580d367fd24.png" alt="12-x-modules-choose-sha-1-repository|690x426" /> </p>
<p>Choose the 'master' branch in the 'sha-1' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/b251d7da11e684add4752a353047cbcfcb1d865e.png" alt="13-x-modules-choose-sha-1-branch|690x420" /> </p>
<p>Make sure the 'sha-1' module is inserted into 'libs/sha-1' so "This Repository Path" should be 'libs/sha-1'.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/5ae807f05915e44e093c1586ccd63d6252021f58.png" alt="14-x-modules-choose-sha-1-module-path|690x452" /> </p>
<p>Click "Add Module". Do not "Apply Changes" yet.</p>
<p>Now add another module to add 'checksum' app to our multi-module structure. To do that click 'Add Module' again:
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/b9b6d76fc48e576f62ef9f9090f0832b5df3da3c_2_1035x642.png" alt="15-x-modules-add-second-module|690x428" /> </p>
<p>Then choose the 'checksum' repository.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/a029ada267357434507335e3320ca121bbd0ac71.png" alt="16-x-modules-choose-checksum-repository|690x462" /></p>
<p>Choose the 'master' branch in it.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/493c65a722114bdac8c757d6df6e5a200d027a26.png" alt="17-x-modules-choose-checksum-branch|690x425" /> </p>
<p>Make sure the 'checksum' repository is inserted into the 'checksum' directory, so "This Repository Path" should be 'checksum'. The directory name is arbitrary (as well as "libs/sha-1"), we will specify it later in <code>add_subdirectory()</code> call.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/d4cc6a6c23f1c0ba52046a9f25f21501b383dd46.png" alt="18-x-modules-choose-checksum-module-path|690x460" /> </p>
<p>Click 'Add Module'</p>
<p><img src="https://support.tmatesoft.com/uploads/default/optimized/1X/75abcf4dd1d5cb9468dee2bce3a8f71f019b83a8_2_1035x690.png" alt="19-x-modules-apply-changes|690x460" /> </p>
<p>Apply the changes.</p>
<p>Now 'checksum-project' repository contains 2 directories: 'checksum' with 'checksum' module and 'libs/sha-1' with 'sha-1' library. They are inserted into 'checksum-project' as normal directories in Git.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/da30b1bfa9a55b259e66b1924b93b2f9dbbf2e20.png" alt="20-checksum-project-repository-toc|677x500" /> </p>
<p><strong>Step 5.</strong> Create a multi-module CMake configuration in 'checksum-project'.</p>
<p>Clone and 'cd' into the 'checksum-project':</p>
<pre><code>git clone http://example.org/scm/<span class="hljs-keyword">checksum</span>/<span class="hljs-keyword">checksum</span>-project.git <span class="hljs-keyword">checksum</span>-<span class="hljs-keyword">project</span>/
cd <span class="hljs-keyword">checksum</span>-<span class="hljs-keyword">project</span>/
</code></pre><p>Create CMakeLists.txt with the following content:</p>
<pre><code><span class="hljs-selector-tag">cmake_minimum_required</span>(VERSION <span class="hljs-number">3.8</span> FATAL_ERROR)
<span class="hljs-selector-tag">project</span>(checksum-project)

<span class="hljs-selector-tag">add_subdirectory</span>(libs/sha-<span class="hljs-number">1</span>)
<span class="hljs-selector-tag">add_subdirectory</span>(checksum)
</code></pre><p>Commit and push the file:</p>
<pre><code>git <span class="hljs-keyword">add</span> CMakeLists.txt
git <span class="hljs-keyword">commit</span> -m "CMakeLists.txt configuration added."
git push origin master
</code></pre><p>That's all!</p>
<p>The project can now be built without installation:</p>
<pre><code>mkdir build
<span class="hljs-built_in">cd</span> build
cmake ..
make
</code></pre><pre><code>[<span class="hljs-meta"> 25%</span>] Building C <span class="hljs-keyword">object</span> libs/sha<span class="hljs-number">-1</span>/CMakeFiles/sha1.dir/sha1.c.o
[<span class="hljs-meta"> 50%</span>] Linking C <span class="hljs-keyword">static</span> library libsha1.a
[<span class="hljs-meta"> 50%</span>] Built target sha1
[<span class="hljs-meta"> 75%</span>] Building C <span class="hljs-keyword">object</span> checksum/CMakeFiles/checksum.dir/main.c.o
[<span class="hljs-meta">100%</span>] Linking C executable checksum
[<span class="hljs-meta">100%</span>] Built target checksum
</code></pre><p>Now let's try our app:</p>
<pre><code><span class="hljs-keyword">checksum</span>/<span class="hljs-keyword">checksum</span> foobar
<span class="hljs-number">8843</span>d7f92416211de9ebb963ff4ce28125932878
</code></pre><p>and compare it with standard 'sha1sum' utility:</p>
<pre><code><span class="hljs-keyword">echo</span> -n foobar | sha1sum
<span class="hljs-number">8843</span>d7f92416211de9ebb963ff4ce28125932878  -
</code></pre><p>All sources are in the same repository as normal Git directories. Now one can modify 'libs/sha-1' and 'checksum/' inside the 'checksum-project' directly and these changes will be synchronized with corresponding 'sha-1' and 'checksum' repositories. And vice versa.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c7242ff3e688fde24ca4b0cf31448facf031f6b6.png" alt="21-structure|600x395" /> 
Have fun!</p>
]]></content:encoded></item><item><title><![CDATA[Setting up multi-module Python environment with X-Modules and Develop Mode]]></title><description><![CDATA[Quite often a Python project consists not only of an app but also of some libraries that could also be used by other projects.

On one hand, it's very convenient to work with the app repository independently of libraries assuming the libraries are in...]]></description><link>https://blog.tmatesoft.com/setting-up-multi-module-python-environment-with-x-modules-and-develop-mode-1</link><guid isPermaLink="true">https://blog.tmatesoft.com/setting-up-multi-module-python-environment-with-x-modules-and-develop-mode-1</guid><category><![CDATA[Python]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Thu, 19 Nov 2020 12:39:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606071559443/Xsh1Ei6q3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Quite often a Python project consists not only of an app but also of some libraries that could also be used by other projects.</p>
<ul>
<li>On one hand, it's very convenient to work with the app repository independently of libraries assuming the libraries are installed on the system (<a target="_blank" href="https://docs.python.org/3/library/venv.html">virtual environment</a>). </li>
<li>On the other hand, this creates a painful problem of "change the library" -&gt; "install the library" -&gt; "check the change from the app development setup" cycle.</li>
</ul>
<p>This post shows, how you can manage this setup with <a target="_blank" href="https://gitmodules.com">Git X-Modules</a>.</p>
<p>For the Git server, we will use Atlassian <a target="_blank" href="https://www.atlassian.com/software/bitbucket/download">Bitbucket Server</a>/<a target="_blank" href="https://www.atlassian.com/enterprise/data-center/bitbucket">Data Center</a> --- one of the most popular self-hosting Git solutions. It's also convenient because there's a special <a target="_blank" href="https://marketplace.atlassian.com/apps/1223696">Git X-Modules App</a> for it. If you are on any other Git Server, you may still use Git X-Modules as a <a target="_blank" href="https://gitmodules.com">command-line tool</a>.</p>
<p><strong>The configuration.</strong> As an example, we'll create an app (let's call it <strong>'qsolver'</strong>) that solves quadratic equations. I will also create a library (<strong>'qsolverlib</strong>) that performs mathematical calculations, so the 'qsolver' app will use it to output the solution.</p>
<p>Finally, there will be a <strong>'qsolver-project'</strong> containing both components. This will help us to pinpoint the version of 'qsolverlib' used by 'qsolver' because otherwise 'qsolver' would use 'qsolverlib' installed on the system that could be of the wrong version. This is especially useful in real life when some problem happens to some older version of the application and it's important to deduce the library version used for that application.</p>
<p><img src="https://support.tmatesoft.com/uploads/default/original/1X/95898270985e8c5a6ed8c9191aeaea97f4d3ae90.png" alt="01-structure|434x264" /> </p>
<p>We will create 3 Git repositories: for 'qsolverlib', 'qsolver', and 'qsolver-project'.</p>
<p><strong>Step 1.</strong> Create 'qsolverlib' Git repository.
Create 'qsolverlib' Git repository on Atlassian Bitbucket Server/Data Center.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/4f0e5f04f16fbdbccaa51bcdfca6f30165ad8cb6.png" alt="02-create-qsolverlib-repository|658x500" /> 
Clone this empty Git repository:</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/qsolver/qsolverlib.git qsolverlib/</span>
cd qsolverlib/
</code></pre><p>Inside this Git repository create the 'qsolverlib' subdirectory and create the <code>solve.py</code> file in it with the following content:</p>
<pre><code><span class="hljs-string">"""Quadratic equation solver."""</span>

<span class="hljs-keyword">import</span> math

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">solve</span>(<span class="hljs-params">a, b, c</span>):</span>
    <span class="hljs-string">"""Solve equation ax^2 + bx + c = 0, return a root or a list with roots"""</span>
    D = b * b - <span class="hljs-number">4</span> * a * c
    <span class="hljs-keyword">if</span> D &lt; <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
    <span class="hljs-keyword">elif</span> D == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> -b / (<span class="hljs-number">2</span> * a)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> [(-b - math.sqrt(D)) / (<span class="hljs-number">2</span> * a), (-b + math.sqrt(D)) / (<span class="hljs-number">2</span> * a)]
</code></pre><p>Also create an empty "__init__.py" file in the "qsolverlib" subdirectory:</p>
<pre><code>touch qsolverlib/<span class="hljs-strong">__init__</span>.py
</code></pre><p>Finally, create "setup.py" at the root of the Git repository:</p>
<pre><code><span class="hljs-string">"""Setup file for qsolverlib."""</span>

<span class="hljs-keyword">import</span> setuptools

setuptools.setup(
    name=<span class="hljs-string">"qsolverlib"</span>,
    version=<span class="hljs-string">"0.0.1"</span>,
    description=<span class="hljs-string">"A library solving quadratic equations"</span>,
    url=<span class="hljs-string">"http://example.org/scm/qsolver/qsolverlib.git"</span>,
    author=<span class="hljs-string">"Dmitry Pavlenko"</span>,
    author_email=<span class="hljs-string">"pavlenko@tmatesoft.com"</span>,
    license=<span class="hljs-string">"MIT"</span>,
    classifiers=[
        <span class="hljs-string">"License :: OSI Approved :: MIT License"</span>,
        <span class="hljs-string">"Programming Language :: Python"</span>
    ],
    packages=setuptools.find_packages(),
    python_requires=<span class="hljs-string">"&gt;=3.7"</span>
)
</code></pre><p>So the resulting structure looks as follows:</p>
<pre><code>.
├── <span class="hljs-selector-tag">qsolverlib</span>
│   ├── __<span class="hljs-selector-tag">init__</span><span class="hljs-selector-class">.py</span>
│   └── <span class="hljs-selector-tag">solve</span><span class="hljs-selector-class">.py</span>
└── <span class="hljs-selector-tag">setup</span><span class="hljs-selector-class">.py</span>
</code></pre><p>Commit and push the package.</p>
<pre><code>git <span class="hljs-keyword">add</span> .
git <span class="hljs-keyword">commit</span> -m "Initial."
git push origin master
</code></pre><p><img src="https://support.tmatesoft.com/uploads/default/original/1X/f910af358fb5259f68772dbd51edb46dd433c649.png" alt="03-qsolverlib-repository-toc|560x500" /> </p>
<p><strong>Step 2.</strong> Create Python virtual environment and install the package there.</p>
<p>Python virtual environment allows installing python packages locally into some directory without requiring to install them at the system-wide level. To create the environment run (not inside the Git repository but inside it parent directory):</p>
<pre><code><span class="hljs-attribute">virtualenv</span> .venv/
</code></pre><p>or</p>
<pre><code><span class="hljs-attribute">python3</span> -m venv .venv/
</code></pre><p>As result, the ".venv/" directory will be created. This environment should be activated to be used:</p>
<pre><code><span class="hljs-keyword">source</span> .venv/bin/activate
</code></pre><p>After that "(.venv)" will be prepended to the command prompt indicating that we're working with the virtual environment.</p>
<p>Now install the 'qsolverlib' package into this environment:</p>
<pre><code><span class="hljs-attribute">pip3</span> install qsolverlib/
</code></pre><p>For me it prints:</p>
<pre><code><span class="hljs-meta">...</span>
  error: invalid command 'bdist_wheel'

  ----------------------------------------
  Failed building wheel for qsolverlib
<span class="hljs-meta">...</span>
</code></pre><p>This can be resolved by installing the 'wheel' package:</p>
<pre><code><span class="hljs-attribute">pip3</span> install wheel
</code></pre><p>Then retrying</p>
<pre><code><span class="hljs-attribute">pip3</span> install qsolverlib/
</code></pre><p>is successful. Now the library can be used by an app if the app is running within the same virtual environment.</p>
<p>The virtual environment can be de-activated with</p>
<pre><code>deactivate
</code></pre><p>command.</p>
<p><strong>Step 3.</strong> Create a 'qsolver' app.</p>
<p>Create 'qsolver' Git repository on Atlassian Bitbucket Server/Data Center.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/c59517831a69d5533a883b76e7f986cab7c8bd8e.png" alt="04-create-qsolver-repository|622x500" /> 
Clone this empty Git repository:</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/qsolver/qsolver.git qsolver/</span>
cd qsolver/
</code></pre><p>Create <code>app.py</code> with the following content:</p>
<pre><code><span class="hljs-keyword">from</span> qsolverlib <span class="hljs-keyword">import</span> solve

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    print(solve.solve(<span class="hljs-number">1</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">2</span>))

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre><p>I.e. it solves: x^2 - 3x + 2 = 0</p>
<p>Commit and push this file:</p>
<pre><code>git <span class="hljs-keyword">add</span> app.py
git <span class="hljs-keyword">commit</span> -m "Initial."
git push origin master
</code></pre><p>Check that the app works:</p>
<pre><code><span class="hljs-selector-tag">python3</span> <span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.py</span> 
<span class="hljs-selector-attr">[1.0, 2.0]</span>
</code></pre><p>Indeed, x^2 - 3x + 2 = (x - 1)(x - 2)</p>
<p><strong>Step 4.</strong> Develop mode of package.</p>
<p>If we now want to change the "solve()" function, we have to edit solve.py in the 'qsolverlib' repository, install the package, and only then test how it works in app.py in 'qsolver'. This is too inconvenient. "Develop mode" is the solution.</p>
<p>To install 'qsolverlib' in develop mode run.</p>
<pre><code><span class="hljs-attribute">pip3</span> install -e qsolverlib/
</code></pre><p>It logically links qsolverlib installed in the virtual environment with the sources of 'qsolverlib', so any changes to 'qsolverlib' sources become effective for the app immediately.</p>
<p><strong>Step 5.</strong> Create a parent project to pinpoint versions of 'qsolverlib' and 'qsolver'.</p>
<p>Create 'qsolver-project' Git repository on Atlassian Bitbucket Server/Data Center.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/d3cb033cbff4035b040167af4f608b111eee1f39.png" alt="06-create-qsolver-project-repository|649x500" /> 
Now we will use <a target="_blank" href="https://tmatesoft.com/x-modules">Git X-Modules</a>, make sure you have it installed. To do that, go to <code>Administration | Find new apps | Search the</code><a target="_blank" href="https://marketplace.atlassian.com/apps/1223696/git-x-modules-for-bitbucket-server?hosting=server&amp;tab=overview">Marketplace</a> and type "X-Modules".
<img src="https://support.tmatesoft.com/uploads/default/original/1X/7c874cca26baf6d433d63b89b39b0eedf20c36d1.png" alt="07-install-x-modules-app|690x203" /> 
Go to the 'qsolver-project' repository page on Bitbucket. As the app is now installed, there's an X-Modules button, click it.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/30c200fa3dd3234e27f1782f55b570513b4dbce7.png" alt="09-x-modules-button|690x181" /> 
The repository is empty, so click "Create Default Branch" to create the "master" branch.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/bb5a571da2a319cbfaed6af9471d17934181f539.png" alt="10-x-modules-create-default-branch|690x285" /> 
Click "Add Module" to add 'qsolverlib' as the first module.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/46e007817169e53196cb4ad23826dfded5c33371.png" alt="11-x-modules-add-first-module|690x275" /> 
Choose the 'qsolverlib' repository and the master branch.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/14119dc64241bf7500eec578a4f9fd2aafacf289.png" alt="11-x-modules-choose-qsolverlib|690x461" /> 
Make sure "This Repository Path" is 'qsolverlib'. It's a path for the insertion point of the 'qsolverlib' module. Click "Add Module".
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/0af10afe2bc5fb1a725b7cc3b3da5d7366bb3124_2_1035x640.png" alt="12-x-modules-add-second-module|690x427" /> 
Instead of applying changes immediately click "Add Module" again, to add the second module (for the app).</p>
<p>Choose the 'qsolver' repository and the master branch.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/02fa96c0c74dd0042077d5a365fb56c8442c872f.png" alt="13-x-modules-choose-qsolver|690x457" /> 
Make sure "This Repository Path" is 'qsolver'. Again, this is a path for the insertion point of the 'qsolver' module.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/2fc6694248d657418ed479e14bad98d297bf6a09.png" alt="14-x-modules-second-module-preview|690x465" /> 
Click "Add Module".
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/322f17f2a78f233877a3e62a9124f317f247f88a_2_1035x684.png" alt="15-x-modules-apply-changes|690x456" /> 
Now apply the changes.</p>
<p>Now 'qsolver-project' repository contains both 'qsolverlib' and 'qsolver' X-Modules. These are regular Git directories that are synchronized with the corresponding repositories.
<img src="https://support.tmatesoft.com/uploads/default/original/1X/6ecb06a74c2457fb9ab017ed1d1e19c790b833af.png" alt="16-qsolver-project-toc|575x499" /> 
So now one can clone the 'qsolver-project' repository and both 'qsolverlib' and 'qsolver' components will be there.</p>
<p><strong>Step 6.</strong> Quickstart script.</p>
<p>Suppose you have a new colleague who wants to start working on both 'qsolverlib' and 'qsolver' as soon as possible. For this purpose, we will create a "create-virtualenv.sh" script that would set up the development environment.</p>
<p>Clone 'qsolver-project':</p>
<pre><code>git <span class="hljs-keyword">clone</span> http:<span class="hljs-comment">//example.org/scm/qsolver/qsolver-project.git qsolver-project</span>
cd qsolver-project
</code></pre><p>The current structure of 'qsolver-project' is:</p>
<pre><code>├── <span class="hljs-selector-tag">qsolver</span>
│   └── <span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.py</span>
└── <span class="hljs-selector-tag">qsolverlib</span>
    ├── <span class="hljs-selector-tag">qsolverlib</span>
    │   ├── __<span class="hljs-selector-tag">init__</span><span class="hljs-selector-class">.py</span>
    │   └── <span class="hljs-selector-tag">solve</span><span class="hljs-selector-class">.py</span>
    └── <span class="hljs-selector-tag">setup</span><span class="hljs-selector-class">.py</span>
</code></pre><p>It already contains 'qsolverlib' and 'qsolver' components, so one doesn't need to clone them separately.</p>
<p>Create "create-virtualenv.sh" script with the following content:</p>
<pre><code><span class="hljs-meta">#!/bin/sh</span>

<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Path for the virtual environment required, e.g.:"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">""</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"    <span class="hljs-variable">$0</span> .venv/"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">""</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

python3 -m venv <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
<span class="hljs-variable">$1</span>/bin/pip3 install wheel
<span class="hljs-variable">$1</span>/bin/pip3 install -e qsolverlib/

<span class="hljs-built_in">echo</span> <span class="hljs-string">"Virtual environment created at <span class="hljs-variable">$1</span>"</span>
</code></pre><p>Add, commit, and push this script:</p>
<pre><code>git <span class="hljs-keyword">add</span> <span class="hljs-keyword">create</span>-virtualenv.sh
git <span class="hljs-keyword">commit</span> -m "create-virtualenv.sh script added."
git push origin master
</code></pre><p>So now for a new colleague, it will be enough to clone 'qsolver-project' and run this script to start working on the project:</p>
<pre><code>sh <span class="hljs-keyword">create</span>-virtualenv.sh .venv/
<span class="hljs-keyword">source</span> .venv/<span class="hljs-keyword">bin</span>/<span class="hljs-keyword">activate</span>
</code></pre><p><strong>Step 7.</strong> An example: make a change to 'qsolverlib'.</p>
<p>Now let's improve our solve() function to handle the case a=0, i.e. to solve bx+c=0 equation.</p>
<p>Edit qsolverlib/qsolverlib/solve.py and qsolver/app.py:
<img src="https://support.tmatesoft.com/uploads/default/original/1X/0bea3fd99bbbb1cfbe80040c827dda0b1b9ddb11.png" alt="17-git-diff|656x500" /> 
Commit and push the changes with to both 'qsolverlib' and 'qsolver' as a single commit in 'qsolver-project'.</p>
<pre><code>git <span class="hljs-keyword">commit</span> -a -m <span class="hljs-string">"solve() improved to handle linear equations as well."</span>
git push origin <span class="hljs-keyword">master</span>
</code></pre><p>Now you can look at 'qsolverlib' and 'qsolver' histories on Bitbucket: they are updated!
<img src="https://support.tmatesoft.com/uploads/default/optimized/1X/a6b97c3b06d64a75a1beeb77211b9019c3a390ad_2_1035x400.png" alt="18-qsolverlib-history|690x267" /> <img src="https://support.tmatesoft.com/uploads/default/optimized/1X/4aac5da2936ba25e2149d9e6c42a34028365b704_2_1035x397.png" alt="19-qsolver-history|690x265" /> 
The synchronization is bi-directional, updates of 'qsolverlib' and 'qsolver' also get into 'qsolver-project' automatically.</p>
]]></content:encoded></item><item><title><![CDATA[How we stopped worrying about submodules and focused on code]]></title><description><![CDATA[A couple of years ago, I was at a large tech conference, presenting SubGit --- our bestselling tool for SVN to Git migration. Among the people who visited our booth was a DevOps lead from the IT department of a large international retailer. He was aw...]]></description><link>https://blog.tmatesoft.com/how-we-stopped-worrying-about-submodules-and-focused-on-code</link><guid isPermaLink="true">https://blog.tmatesoft.com/how-we-stopped-worrying-about-submodules-and-focused-on-code</guid><category><![CDATA[Git]]></category><dc:creator><![CDATA[TMate Software]]></dc:creator><pubDate>Tue, 17 Nov 2020 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606071117220/j2Nul_9yT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A couple of years ago, I was at a large tech conference, presenting SubGit --- our bestselling tool for SVN to Git migration. Among the people who visited our booth was a DevOps lead from the IT department of a large international retailer. He was aware of the advantages of Git but was not planning to move his team to it.  "We have a lot of small projects," - he said - "and they are all interconnected. Having each of them in a separate Git repository would be a nightmare - for each minor update, one would have to push to a dozen different repositories. Besides, we make huge use of svn:externals, and there's no sound alternative to it in Git."</p>
<p>That sounded weird. Not being a developer myself, I asked a few people what they do when they have several Git repositories for different parts of the same project.</p>
<ul>
<li>One said: "I just run several instances of my IDE at the same time and switch between them."</li>
<li>Another one - "I am trying to avoid that and have all projects in the same repository. Takes a while to clone it, though."</li>
<li>The third one said: "I use Git Submodules."</li>
</ul>
<p>Bingo! So why couldn't this guy from the conference just use Submodules? Very soon, I knew why. Git Submodules, intended to do just that, turned out to be very inconvenient and unsafe. Not only each user had to install it and run submodules-specific commands from his working copy --- an accidental mistake, such as updating the main repository before the submodules, could ruin the whole setup.</p>
<p>We are a small team, and we don't run many projects simultaneously, but still, we have a few libraries shared between the projects. So we became really perplexed with the question: "how to update the main repository and the shared library with one push." At some point, one of us asked: "Can we use the same technology that we developed to mirror SVN and Git, to sync one folder in a Git repository with another Git repository?"</p>
<p>Yes, we can.</p>
<p>Almost two years later (as I said, we are a small team and had other products to support and improve), we had a working alpha of "Git X-Modules" --- a server-side solution to handle the externals/submodules case without any overhead to end-users. We showed it to several friends and college mates and asked if they would use it in their work to combine multiple repositories in one. Then some of them said: "I would have used it a few years ago, but the current trend is to put everything in a Monorepo."</p>
<p>Bummer. Was it just that simple? Actually, no. Monorepo is still rather a battlefield than an industry standard. It's being discussed all over, and for every <a target="_blank" href="https://medium.com/@adamhjk/monorepo-please-do-3657e08a4b70">post that praises this approach</a>, there's another <a target="_blank" href="https://medium.com/%40mattklein123/monorepos-please-dont-e9a279be011b">post that condemns it</a>. The main disadvantage is not even the size of such a repository, but the mess created by dozens (or thousands) of developers from different projects pushing to the same place. So we asked ourselves: "Can we achieve the advantages of a monorepo with X-Modules?"</p>
<p>Yes, we can.</p>
<p>By that time, we have been using X-Modules on our company servers for quite a while, having a lot of fun combining repositories in various combinations, like Lego blocks. A shared library was only one way to use it; another scenario was to take an empty repository and fill it with X-Modules, synced with other repositories - essentially, a monorepo. There was also an opposite case - have a large repository split by X-Modules into several small projects.</p>
<p>Another round of interviews: "Your command-line tool is great, but I would rather use it as a plugin to my Bitbucket." That was an easy one --- we've done it before with SubGit. A beta version of Git X-Modules App for Bitbucket Server was uploaded to the Atlassian Marketplace in October 2020... one day before Atlassian announced that Server is going to EOL, and everybody is encouraged to move to the Cloud.</p>
<p>So our journey goes on. Now we have to figure out how to make our app work with cloud-based Git services. We have one or two ideas up our sleeves :-). Actually, this post should have been called "<em>How we stopped worrying about submodules and focused on making a better solution.</em>" But if you have a self-hosted Git server, you may try this solution out already at <a target="_blank" href="https://gitmodules.com/">https://gitmodules.com</a>. </p>
<p>What do you think, was it worth it? Or should we just have "gone monorepo", like anyone else?</p>
]]></content:encoded></item></channel></rss>