Кроссплатформенность — способность программы запускаться на разных платформах, например, разных операционных системах. Это довольно важное качество для программ, которые нужно запускать и в Windows, и в Linux. Причем как со стороны пользователей (все хотят кроссплатформенный фотошоп), так и со стороны разработчиков. Последнее часто встречается в веб-разработке, где часть команды может использовать одну операционную систему, а часть — другую.
Кроссплатформенность программы зависит от разработчиков. В статье мы разберем несколько типичных ошибок программистов, которые ухудшают кроссплатформенность или вообще убирают ее.
Подписывайтесь на канал Кирилла Мокевнина в Telegram — чтобы узнать больше о программировании и профессиональном пути разработчика
Современные высокоуровневые языки изначально кроссплатформенны. Они прячут различия в операционных системах за готовыми абстракциями. Например, чтение и запись файлов внутри этих языков не зависит от того, где запускается код:
import fs from 'fs';
// Не забывайте что абстракции текут, в определенных моментах различия все же есть
fs.readFileSync('filename');
Но есть определенные вещи, которые спрятать нельзя, и программисту приходится работать с ними напрямую. Возьмем для примера разделитель директорий. В Windows это \
обратный слеш (backslash), в Linux – /
прямой слеш или просто слеш. Какой использовать? Попробуем разобраться.
Разделитель директорий
Во многих языках пытаются упростить эту задачу, добавляя специальные идентификаторы (переменные, константы), содержащие символы, специфичные для конкретной операционной системы. В зависимости от того, где запускается программа, значение этих идентификаторов может отличаться. В JavaScript это работает так:
import path from 'path';
console.log(path.sep);
// В Linux: /
// В Macos: /
// В Windows: \
path.join('one', 'two', 'three');
// Внутри делает ['one', 'two', 'three'].join(path.sep);
// В Linux: one/two/three
// В Macos: one/two/three
// В Windows: one\two\three
Решение кажется очевидным: достаточно начать использовать path.sep
в тех местах, где идет работа с путями, и вопрос исчерпан. Да, для подавляющего большинства ситуаций такой ход сработает, но не для всех. Представьте, что наша программа должна формировать конфигурационный json-файл, внутри которого есть пути:
{
"manifest": "public/assets/manifest.json"
}
Затем этот файл будет использоваться на самом сайте (в продакшене). В подавляющем большинстве случаев продакшен (место, где запускается сайт) работает на Linux. Это значит, что если файл был сформирован в Windows-среде, то он не заработает в Linux-среде, так как Linux не понимает обратные слеши (как и MacOS). Что делать в этой ситуации?
Секрет в том, что Windows понимает и обратный, и прямой слеш, а это значит, что кроссплатформенность в данном случае обеспечивается только при использовании прямых слешей. Поэтому лучше отказаться от использования path.sep
, и в тех местах, где формируются пути, использовать прямой слеш. С другой стороны, не нужно сражаться с функциями типа path.join
, как в примере выше. Пусть они используют path.sep
внутри себя, зато мы получаем удобный способ работы с путями.
Перевод строки
Другой пример — перевод строки. В большинстве операционных систем для перевода строк используется \n
, но только не в Windows. Там по историческим причинам перевод строки — это \r\n
. И это уже действительно проблема, потому что касается не только того, как работает код, но и в принципе самого исходного кода. Каждый файл, который сохраняется в Windows, будет сохраняться с \r\n
в конце каждой строки. Это приводит к куче разных проблем:
- появляются различные артефакты при выводе данных;
- сходит с ума гит и редактор;
- усложняется отладка тестов, так как данные визуально выглядят одинаково, но не совпадают при сравнении.
С этими и другими проблемами сталкиваются все разработчики без исключения, которые работают в командах, разрабатывающих код на разных платформах. Для решения этих проблем есть два подхода. Первый — использовать перевод строки конкретной операционной системы:
import os from 'os';
console.log(os.EOL);
// В Linux: \n
// В Macos: \n
// В Windows: \r\n
Как раз этот подход приводит к проблемам, указанным выше.
Второй подход — всегда использовать \n
. Ситуация здесь аналогична разделителю директорий. \n
— универсальный символ, одинаково хорошо работает и в Windows, и в остальных операционных системах:
lines = ['one', 'two', 'three'];
const text = lines.join('\n');
// one
// two
// three
Но это еще не все. В обязательном порядке нужно правильно настроить и редакторы, и гит. Только после этого можно начинать говорить о кроссплатформенности.