<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Best Practices on kenji.blog</title><link>http://kenji.blog/en/categories/best-practices/</link><description>Recent content in Best Practices on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 08:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/en/categories/best-practices/index.xml" rel="self" type="application/rss+xml"/><item><title>What to Keep in Mind for Cross-Platform Development on Mac and Windows</title><link>http://kenji.blog/en/p/cross-platform-development-mac-windows/</link><pubDate>Sun, 13 Sep 2026 08:00:00 +0900</pubDate><guid>http://kenji.blog/en/p/cross-platform-development-mac-windows/</guid><description>&lt;img src="http://kenji.blog/p/cross-platform-development-mac-windows/img/eyecatch.jpg" alt="Featured image of post What to Keep in Mind for Cross-Platform Development on Mac and Windows" />&lt;p>Cross-platform development spanning multiple operating systems (OS) such as Mac (macOS), Windows, and even Linux (including WSL) is an unavoidable path in modern software engineering. When building web development, mobile app backends, or cross-platform desktop apps (Electron, Tauri, Qt, etc.), if a team uses different OSs, you will encounter numerous &amp;ldquo;bugs caused by OS differences.&amp;rdquo;&lt;/p>
&lt;p>Each OS has its own historical background and design philosophy. Windows has a unique architecture derived from MS-DOS (Win32 API, NT Kernel), while macOS is based on UNIX (FreeBSD-based Darwin), and Linux conforms to POSIX standards. These fundamental differences create &amp;ldquo;pitfalls&amp;rdquo; that plague developers in all situations, such as file systems, networking, and process handling.&lt;/p>
&lt;p>In this article, we will provide extremely detailed and practical explanations of the technical differences and best practices you absolutely need to know for development teams with a mix of Mac and Windows, or application development targeting both OSs.&lt;/p>
&lt;hr>
&lt;h2 id="1-the-line-ending-pitfall-crlf-vs-lf-and-strict-git-configuration">1. The Line Ending Pitfall (CRLF vs LF) and Strict Git Configuration
&lt;/h2>&lt;p>One of the most frequent causes of confusion in team development is the issue of &amp;ldquo;Line Endings&amp;rdquo;. This is a historical problem dating back to the typewriter era.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Windows&lt;/strong>: Uses &lt;strong>CRLF&lt;/strong>, a combination of Carriage Return (CR, &lt;code>\r&lt;/code>, &lt;code>0x0D&lt;/code>) and Line Feed (LF, &lt;code>\n&lt;/code>, &lt;code>0x0A&lt;/code>), as the standard line ending.&lt;/li>
&lt;li>&lt;strong>macOS / Linux&lt;/strong>: Uses &lt;strong>LF&lt;/strong>, which is a Line Feed alone, as the standard line ending. (*Up to the early Mac OS 9, it was CR alone, but since Mac OS X it became UNIX-based and uses LF).&lt;/li>
&lt;/ul>
&lt;p>Because of this difference, when sharing source code in a Git repository, differences (diffs) can extend to the entire file, or a shell script (&lt;code>.sh&lt;/code>) intended to run in a Linux environment can become CRLF by being edited on Windows, causing &lt;code>\r&lt;/code> to be interpreted as an invalid character at runtime, leading to errors like &lt;code>\r: command not found&lt;/code>.&lt;/p>
&lt;h3 id="solution-in-git-management-with-gitattributes">Solution in Git: Management with &lt;code>.gitattributes&lt;/code>
&lt;/h3>&lt;p>Git has a setting called &lt;code>core.autocrlf&lt;/code>, but relying on it is dangerous. Because it depends on the global settings of individual developers&amp;rsquo; local machines, troubles are prone to occur due to missing settings when new members join the team.&lt;/p>
&lt;p>The best practice is to place a &lt;code>.gitattributes&lt;/code> file in the root directory of the repository and explicitly define the handling of line endings at the repository level. This guarantees consistent behavior no matter which environment it is cloned in.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># Treat as text files by default, and normalize to LF in the repository (Git database)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># It will be converted to each OS&amp;#39;s standard line ending upon checkout
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* text=auto
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># However, for specific extensions like source code, always force LF regardless of the OS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.sh text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.py text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.cpp text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.hpp text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.js text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.json text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Force CRLF for Windows-specific batch files, etc.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.cmd text eol=crlf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.bat text eol=crlf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Do not convert line endings for files such as images and pre-built binaries (prevents corruption)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.png binary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.jpg binary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.pdf binary
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="2-file-system-case-sensitivity">2. File System Case Sensitivity
&lt;/h2>&lt;p>Case sensitivity in file systems is also one of the biggest hurdles in cross-platform development.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>macOS (APFS / HFS+)&lt;/strong>: By default, &lt;strong>Case-Insensitive&lt;/strong> but &lt;strong>Case-Preserving&lt;/strong>. That is, if saved as &lt;code>File.txt&lt;/code>, it will be displayed as &lt;code>File.txt&lt;/code>, but you can also load it by accessing it as &lt;code>file.txt&lt;/code> from a program.&lt;/li>
&lt;li>&lt;strong>Windows (NTFS)&lt;/strong>: Similar to macOS, it is &lt;strong>Case-Insensitive&lt;/strong> and &lt;strong>Case-Preserving&lt;/strong> by default.&lt;/li>
&lt;li>&lt;strong>Linux / WSL (ext4 etc.)&lt;/strong>: &lt;strong>Strictly Case-Sensitive&lt;/strong>. &lt;code>File.txt&lt;/code> and &lt;code>file.txt&lt;/code> can coexist in the same directory as completely different files.&lt;/li>
&lt;/ul>
&lt;h3 id="typical-bugs-that-occur">Typical Bugs that Occur
&lt;/h3>&lt;p>When developing on Mac or Windows, even if you specify &lt;code>#include &amp;quot;myclass.h&amp;quot;&lt;/code> (or &lt;code>import &amp;quot;./myclass&amp;quot;&lt;/code>) in lowercase in the source code, if the actual file is &lt;code>MyClass.h&lt;/code>, the build will succeed because the local OS is Case-Insensitive.&lt;/p>
&lt;p>However, if you commit this code and run the build on a CI/CD server (usually Linux such as Ubuntu), you will get a &amp;ldquo;file not found&amp;rdquo; compilation error because the ext4 file system of Linux is Case-Sensitive.&lt;/p>
&lt;h3 id="algorithmic-perspective-file-search-complexity-and-normalization">Algorithmic Perspective: File Search Complexity and Normalization
&lt;/h3>&lt;p>Let&amp;rsquo;s think mathematically about what kind of processing takes place internally when a file system resolves a file path.&lt;/p>
&lt;p>In the case of case-sensitive ext4, the entries in the directory are managed by structures such as hash tables or B-Trees. Assuming the number of files in the directory is $N$ and the length of the file name is $L$, the computational complexity for a simple binary search or tree search is as follows.&lt;/p>
$$ T_{search}(N) = O(L \log N) $$&lt;p>On the other hand, in case-insensitive file systems like NTFS and APFS, it is necessary to process Case Folding, which normalizes both strings to the same case (uppercase or lowercase) before comparing the strings. Case conversion considering Unicode normalization and locales cannot be completed with simple ASCII bit operations, and requires a table lookup.&lt;/p>
&lt;p>Assuming the calculation cost of the conversion function is a constant $C_{fold}$, an extra overhead is incurred for each string comparison.&lt;/p>
$$ T_{insensitive\_search}(N) = O( (L \times C_{fold}) \log N ) $$&lt;p>Recent OSs highly cache this, but the fundamental behavioral difference can only be restricted by development conventions. The safest approach is to establish a project convention: &lt;strong>&amp;ldquo;Unify all file names and directory names to lowercase and hyphens (kebab-case) or underscores (snake-case).&amp;rdquo;&lt;/strong>&lt;/p>
&lt;hr>
&lt;h2 id="3-path-separators-and-file-path-abstraction">3. Path Separators and File Path Abstraction
&lt;/h2>&lt;p>The handling of separators indicating the hierarchy of directories reflects a fundamental difference between OSs.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Windows&lt;/strong>: Uses a backslash &lt;code>\&lt;/code> (which appears as a yen symbol &lt;code>¥&lt;/code> depending on the font in Japanese environments), and also has the concepts of drive letters (e.g., &lt;code>C:\&lt;/code>) and UNC paths (e.g., &lt;code>\\Server\Share&lt;/code>).&lt;/li>
&lt;li>&lt;strong>macOS / Linux&lt;/strong>: Uses a slash &lt;code>/&lt;/code>, and all file systems have a Single Root Hierarchy starting from a single root &lt;code>/&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Many programming languages will interpret &lt;code>/&lt;/code> as a file separator even on Windows (partly because the Win32 API itself supports &lt;code>/&lt;/code>). However, it can cause fatal errors when passing paths as command-line arguments, invoking system calls directly, or comparing/parsing paths as strings.&lt;/p>
&lt;h3 id="best-practices-by-language-os-abstraction">Best Practices by Language (OS Abstraction)
&lt;/h3>&lt;p>&lt;strong>Absolutely avoid&lt;/strong> constructing file paths through string concatenation (e.g., &lt;code>path + &amp;quot;\\&amp;quot; + filename&lt;/code>). Use the standard libraries for path manipulation (OS Abstraction Layer) provided in each language.&lt;/p>
&lt;h4 id="example-in-c-stdfilesystem">Example in C++ (&lt;code>std::filesystem&lt;/code>)
&lt;/h4>&lt;p>In C++17 and later, &lt;code>&amp;lt;filesystem&amp;gt;&lt;/code> was introduced, making it possible to abstract path differences between platforms.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;filesystem&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">namespace&lt;/span> &lt;span class="n">fs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">filesystem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Constructing an OS-independent path (abstraction via operator overloading)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">fs&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">path&lt;/span> &lt;span class="n">dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fs&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">path&lt;/span> &lt;span class="n">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;config.json&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fs&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">path&lt;/span> &lt;span class="n">full_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dir&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Becomes &amp;#34;data\config.json&amp;#34; on Windows, &amp;#34;data/config.json&amp;#34; on Mac/Linux
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Full path: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">full_path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="example-in-python-pathlib">Example in Python (&lt;code>pathlib&lt;/code>)
&lt;/h4>&lt;p>In the past, &lt;code>os.path.join()&lt;/code> was used, but today it is standard to use the object-oriented &lt;code>pathlib&lt;/code> module.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pathlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The / operator is overridden to generate a path object tailored to the OS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">base_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;user_data&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">config_file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">base_dir&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="s2">&amp;#34;settings&amp;#34;&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="s2">&amp;#34;app.ini&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Path resolution and file reading are also possible with consistent methods&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">config_file&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exists&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">config_file&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read_text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="example-in-nodejs-path-module">Example in Node.js (&lt;code>path&lt;/code> module)
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;path&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// path.join takes arguments and joins them with the appropriate separator for the current OS
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">configPath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;config&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;default.json&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">configPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Windows: &amp;#34;config\default.json&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// macOS/Linux: &amp;#34;config/default.json&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="4-character-encoding-utf-8-vs-cp932shift-jis-and-the-unicode-barrier">4. Character Encoding (UTF-8 vs CP932/Shift-JIS) and the Unicode Barrier
&lt;/h2>&lt;p>The biggest headache in Japanese environments on Windows is character encoding.
In modern development, macOS and Linux are entirely unified to &lt;strong>UTF-8&lt;/strong> throughout the system, terminal, and file encodings. However, the standard encoding of the Japanese version of Windows (&amp;ldquo;ANSI Code Page&amp;rdquo; based on system locale) still often operates with &lt;strong>CP932 (Microsoft extension of Shift-JIS)&lt;/strong> as the default.
*The internal string representation of the Win32 API is UTF-16LE (&lt;code>wchar_t&lt;/code>).&lt;/p>
&lt;p>When reading and writing files in Python and other languages, if the encoding is not explicitly specified, Windows attempts to interpret it according to the result of &lt;code>locale.getpreferredencoding()&lt;/code> (CP932). As a result, attempting to read a file saved in UTF-8 can cause a &lt;code>UnicodeDecodeError&lt;/code> or result in garbled text (Mojibake).&lt;/p>
&lt;h3 id="mathematical-model-of-character-encoding-conversion-and-overhead">Mathematical Model of Character Encoding Conversion and Overhead
&lt;/h3>&lt;p>When converting a string from one encoding (UTF-8) to another (UTF-16 or CP932), the worst-case computational complexity is proportional to the length of the string. If the byte length of the string is $B$, the complexity of the conversion is $O(B)$. However, the parsing of the variable-length encoding UTF-8, surrogate pair calculations, and conversion table lookups cause overhead that cannot be ignored.&lt;/p>
&lt;p>Assuming the string length is $N$, the mapping function from multi-byte characters to Unicode code points is $f_{decode}$, and the mapping function from code points to the target encoding is $f_{encode}$, the total conversion time $T_{conv}$ is approximated as follows.&lt;/p>
$$ T_{conv} = \sum_{i=1}^{N} \Big( C_{decode} \cdot f_{decode}(x_i) + C_{encode} \cdot f_{encode}(y_i) \Big) \approx O(N) $$&lt;p>In cross-platform applications, you must be aware that this conversion cost is incurred every time a native OS API is called (crossing an I/O boundary) (especially when developing in C++ for Windows, conversions to UTF-16 using &lt;code>MultiByteToWideChar&lt;/code> etc. frequently occur).&lt;/p>
&lt;h3 id="countermeasures-for-encoding">Countermeasures for Encoding
&lt;/h3>&lt;p>The most reliable countermeasure is to &lt;strong>&amp;ldquo;always explicitly specify UTF-8 at all times.&amp;rdquo;&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Good example in Python: Always specify encoding=&amp;#34;utf-8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;data.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Hello, World!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Additionally, to display UTF-8 output correctly in a Windows terminal (Command Prompt or PowerShell), you may need workarounds such as setting the environment variable &lt;code>PYTHONUTF8=1&lt;/code> when launching the application, or temporarily changing the console&amp;rsquo;s code page to UTF-8 using the &lt;code>chcp 65001&lt;/code> command for Node.js.&lt;/p>
&lt;hr>
&lt;h2 id="5-environment-variables-and-shell-environment-differences-bashzsh-vs-powershell">5. Environment Variables and Shell Environment Differences (bash/zsh vs PowerShell)
&lt;/h2>&lt;p>The difference in shells (command-line interpreters) when running build scripts or development tools is also a major barrier in cross-platform development.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>macOS / Linux&lt;/strong>: &lt;code>bash&lt;/code> or &lt;code>zsh&lt;/code> are mainstream. They perform text-based pipeline processing.&lt;/li>
&lt;li>&lt;strong>Windows&lt;/strong>: Command Prompt (&lt;code>cmd.exe&lt;/code>) or &lt;code>PowerShell&lt;/code>. PowerShell is .NET-based and has a powerful object-oriented pipeline, but its syntax is completely different from POSIX shells.&lt;/li>
&lt;/ul>
&lt;p>Because the methods for referencing and setting environment variables differ, writing OS-dependent code in the &lt;code>scripts&lt;/code> section of Node.js&amp;rsquo;s &lt;code>package.json&lt;/code> will cause it to break in other environments.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ❌ Bad example: On Windows, &amp;#34;NODE_ENV&amp;#34; is not recognized as a command, resulting in an error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NODE_ENV=production webpack&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="solution-utilizing-cross-platform-tools">Solution: Utilizing Cross-Platform Tools
&lt;/h3>&lt;p>In a Node.js environment, use packages like &lt;code>cross-env&lt;/code> to abstract the setting of environment variables.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ✅ Good example: cross-env absorbs OS differences and sets the environment variable appropriately before launching webpack
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cross-env NODE_ENV=production webpack&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;clean&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;rimraf dist/&amp;#34;&lt;/span> &lt;span class="c1">// Use a cross-platform remover instead of rm -rf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If a large-scale project requires complex shell scripts, the current best practice is to make the use of WSL (Windows Subsystem for Linux) or Git Bash standard for developers on Windows environments, and uniformly manage all batch processing as &lt;code>.sh&lt;/code> scripts.&lt;/p>
&lt;hr>
&lt;h2 id="6-cross-platform-build-systems-and-compilers">6. Cross-Platform Build Systems and Compilers
&lt;/h2>&lt;p>When dealing with native code (languages compiled directly into machine code) such as C++ and Rust, you must overcome not only OS-specific APIs but also differences in build systems and compilers.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Compilers&lt;/strong>:
&lt;ul>
&lt;li>Windows: MSVC (Microsoft Visual C++), MinGW (GCC for Windows)&lt;/li>
&lt;li>macOS: Apple Clang&lt;/li>
&lt;li>Linux: GCC, Clang&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Binary Formats&lt;/strong>:
&lt;ul>
&lt;li>Windows: PE (Portable Executable) &lt;code>.exe&lt;/code> / &lt;code>.dll&lt;/code>&lt;/li>
&lt;li>macOS: Mach-O&lt;/li>
&lt;li>Linux: ELF (Executable and Linkable Format) &lt;code>.so&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="utilizing-meta-build-systems-with-cmake">Utilizing Meta-Build Systems with CMake
&lt;/h3>&lt;p>In C/C++ projects, the global de facto standard for achieving cross-platform compatibility is &lt;strong>CMake&lt;/strong>. CMake does not compile source code directly, but functions as a &amp;ldquo;Generator&amp;rdquo; that generates native build configuration files tailored to each environment (Visual Studio solution files for Windows, Makefile or Ninja build scripts for Linux/Mac).&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;CMakeLists.txt (Platform Independent)&amp;#34;] --&amp;gt; B(&amp;#34;CMake Engine&amp;#34;)
B --&amp;gt; C{&amp;#34;Target Operating System&amp;#34;}
C --&amp;gt;|Windows| D[&amp;#34;Visual Studio Solution / MSBuild&amp;#34;]
C --&amp;gt;|macOS| E[&amp;#34;Xcode Project / Apple Clang&amp;#34;]
C --&amp;gt;|Linux| F[&amp;#34;Makefile / Ninja / GCC&amp;#34;]
D --&amp;gt; G[&amp;#34;Windows Executable (.exe)&amp;#34;]
E --&amp;gt; H[&amp;#34;macOS Executable (Mach-O)&amp;#34;]
F --&amp;gt; I[&amp;#34;Linux Executable (ELF)&amp;#34;]
&lt;/pre>
&lt;p>By using CMake, you can absorb environmental differences and generate the optimal binaries for each OS from a single configuration file (&lt;code>CMakeLists.txt&lt;/code>). Resolving dependencies (&lt;code>find_package&lt;/code>) and linking OS-specific libraries can also be easily written using conditional branching.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Example part of CMakeLists.txt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">WIN32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="c"># Link Windows-specific libraries (such as WS2_32.lib)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">my_app&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">ws2_32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">OS_WINDOWS&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">elseif&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">APPLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="c"># Link macOS-specific frameworks
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">my_app&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s2">&amp;#34;-framework Foundation&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">OS_MACOS&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">elseif&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">UNIX&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">NOT&lt;/span> &lt;span class="s">APPLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="c"># Links for Linux (such as pthread)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">my_app&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">pthread&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">OS_LINUX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-utilizing-architecture-patterns-os-abstraction-layer-osal">7. Utilizing Architecture Patterns: OS Abstraction Layer (OSAL)
&lt;/h2>&lt;p>Completely separating system-dependent processing (file operations, process/thread creation, memory management, socket communication, etc.) from the core business logic of the application is the cornerstone of cross-platform development.&lt;/p>
&lt;p>To achieve this, we use a pattern called the &lt;strong>OS Abstraction Layer (OSAL)&lt;/strong>.&lt;/p>
&lt;p>Below is an example of class design that wraps the specific APIs of each OS and provides a common interface. Implementations are switched using polymorphism or compile-time macro switches.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class SystemInterface {
&amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
+createDirectory(path: string) bool
+getSystemMemoryUsage() uint64
+spawnProcess(command: string) int
}
class WindowsSystem {
+createDirectory(path: string) bool
+getSystemMemoryUsage() uint64
+spawnProcess(command: string) int
}
class PosixSystem {
+createDirectory(path: string) bool
+getSystemMemoryUsage() uint64
+spawnProcess(command: string) int
}
SystemInterface &amp;lt;|-- WindowsSystem
SystemInterface &amp;lt;|-- PosixSystem
&lt;/pre>
&lt;p>By isolating platform-specific code in one place (usually directories like &lt;code>src/platform/windows/&lt;/code> or &lt;code>src/platform/posix/&lt;/code>), you can keep the remaining 95% of the code (GUI logic, data processing, communication protocol parsing, etc.) completely cross-platform and testable.&lt;/p>
&lt;hr>
&lt;h2 id="8-cross-platform-verification-in-cicd-matrix-build">8. Cross-Platform Verification in CI/CD (Matrix Build)
&lt;/h2>&lt;p>No matter how carefully developers code in their local environment, the ultimate stronghold for cross-platform support is the &lt;strong>CI/CD (Continuous Integration / Continuous Deployment) pipeline&lt;/strong>. There is no end to cases where code runs in the local environment (e.g., Mac) but results in compilation errors on other OSs (Windows).&lt;/p>
&lt;p>Utilize modern CI tools such as GitHub Actions or GitLab CI, and set up a Matrix Build that &lt;strong>executes builds and tests in parallel on all Windows, macOS, and Linux environments&lt;/strong> every time a Pull Request is created.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Example cross-platform CI setup with GitHub Actions&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Cross-Platform Build and Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">push, pull_request]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail-fast&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Continue testing on other OSs even if one OS fails&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Specify three runners: Windows, macOS, and Linux&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">ubuntu-latest, windows-latest, macos-latest]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Set up Python Environment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cache&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;pip&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Cache dependencies across platforms&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install dependencies&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python -m pip install --upgrade pip &amp;amp;&amp;amp; pip install -r requirements.txt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Run Test Suite&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pytest -v&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Visualizing this CI/CD flow looks like the following.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant Dev as &amp;#34;Developer&amp;#34;
participant GitHub as &amp;#34;GitHub Actions (Coordinator)&amp;#34;
participant Ubuntu as &amp;#34;Linux Runner (VM)&amp;#34;
participant Windows as &amp;#34;Windows Runner (VM)&amp;#34;
participant Mac as &amp;#34;macOS Runner (VM)&amp;#34;
Dev-&amp;gt;&amp;gt;GitHub: &amp;#34;git push origin feature-branch&amp;#34;
GitHub-&amp;gt;&amp;gt;Ubuntu: &amp;#34;Dispatch Job (ubuntu-latest)&amp;#34;
GitHub-&amp;gt;&amp;gt;Windows: &amp;#34;Dispatch Job (windows-latest)&amp;#34;
GitHub-&amp;gt;&amp;gt;Mac: &amp;#34;Dispatch Job (macos-latest)&amp;#34;
par Parallel Execution Matrix
Ubuntu--&amp;gt;&amp;gt;Ubuntu: &amp;#34;Checkout, Setup Env, Build, Test&amp;#34;
Windows--&amp;gt;&amp;gt;Windows: &amp;#34;Checkout, Setup Env, Build, Test&amp;#34;
Mac--&amp;gt;&amp;gt;Mac: &amp;#34;Checkout, Setup Env, Build, Test&amp;#34;
end
Ubuntu--&amp;gt;&amp;gt;GitHub: &amp;#34;Result: Success (Pass)&amp;#34;
Windows--&amp;gt;&amp;gt;GitHub: &amp;#34;Result: Failure (Fail - encoding error)&amp;#34;
Mac--&amp;gt;&amp;gt;GitHub: &amp;#34;Result: Success (Pass)&amp;#34;
GitHub--&amp;gt;&amp;gt;Dev: &amp;#34;Status: Failed (Windows check failed)&amp;#34;
&lt;/pre>
&lt;p>By automatically collecting the test results on each OS and configuring branch protection rules to &lt;strong>allow merging into the main branch only if all environments turn green (success)&lt;/strong>, you can proactively prevent platform-dependent bugs from slipping into the production environment or release builds.&lt;/p>
&lt;hr>
&lt;h2 id="summary">Summary
&lt;/h2>&lt;p>Cross-platform development for Mac and Windows has a wide variety of challenges rooted in historical backgrounds.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Line Endings&lt;/strong>: Force repository-level normalization (such as LF unification) using &lt;code>.gitattributes&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Case Sensitivity&lt;/strong>: Do not rely on the &amp;ldquo;case-insensitive&amp;rdquo; behavior of macOS/Windows; establish strict file naming conventions and practice strict case matching.&lt;/li>
&lt;li>&lt;strong>Path Separators&lt;/strong>: Utilize standard language path manipulation APIs (&lt;code>std::filesystem&lt;/code>, &lt;code>pathlib&lt;/code>, &lt;code>path&lt;/code> module) to absorb OS differences.&lt;/li>
&lt;li>&lt;strong>Encoding&lt;/strong>: Always specify UTF-8 and thoroughly eliminate the influence of CP932, the default behavior in Windows.&lt;/li>
&lt;li>&lt;strong>Environment Variables &amp;amp; Shells&lt;/strong>: Use abstraction tools like &lt;code>cross-env&lt;/code> or unify the execution environment to WSL/Docker, etc.&lt;/li>
&lt;li>&lt;strong>Build Systems&lt;/strong>: For C/C++, utilize meta-build systems like CMake to generate the optimal native toolchain for each OS.&lt;/li>
&lt;li>&lt;strong>OS-Dependent Code&lt;/strong>: Design an OS Abstraction Layer (OSAL) to separate and isolate platform-dependent logic.&lt;/li>
&lt;li>&lt;strong>CI/CD&lt;/strong>: Introduce matrix builds to automate clean builds and testing across all target OSs, eliminating person-dependency.&lt;/li>
&lt;/ol>
&lt;p>Today, powerful frameworks such as Electron, Tauri, and .NET absorb many of these differences, but knowledge of the native behavior of the underlying OS (file systems and encoding) remains indispensable when resolving severe performance issues or obscure bugs. By sharing and strictly enforcing these best practices across the team from the early stages of a project, you can significantly reduce unproductive debugging time caused by OS differences and focus on essential software value creation.&lt;/p></description></item></channel></rss>