cmake_host_system_information: Can read /etc/os-release
file
This commit is contained in:
parent
e808cbb1dd
commit
1e65e4a6e5
@ -132,7 +132,52 @@ queried. The list of queried values is stored in ``<variable>``.
|
|||||||
|
|
||||||
See :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`
|
See :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`
|
||||||
|
|
||||||
|
For Linux distributions additional ``<key>`` values are available to get operating
|
||||||
|
system identification as described in the `man 5 os-release`_.
|
||||||
|
|
||||||
|
``DISTRIB_INFO``
|
||||||
|
.. versionadded:: 3.22
|
||||||
|
|
||||||
|
Read :file:`/etc/os-release` file and define the given ``<variable>``
|
||||||
|
into a list of read variables
|
||||||
|
|
||||||
|
``DISTRIB_<name>``
|
||||||
|
.. versionadded:: 3.22
|
||||||
|
|
||||||
|
Get the ``<name>`` variable if it exists in the :file:`/etc/os-release` file
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
cmake_host_system_information(RESULT PRETTY_NAME QUERY DISTRIB_PRETTY_NAME)
|
||||||
|
message(STATUS "${PRETTY_NAME}")
|
||||||
|
|
||||||
|
cmake_host_system_information(RESULT DISTRO QUERY DISTRIB_INFO)
|
||||||
|
|
||||||
|
foreach(VAR IN LISTS DISTRO)
|
||||||
|
message(STATUS "${VAR}=`${${VAR}}`")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
|
Output::
|
||||||
|
|
||||||
|
-- Ubuntu 20.04.2 LTS
|
||||||
|
-- DISTRO_BUG_REPORT_URL=`https://bugs.launchpad.net/ubuntu/`
|
||||||
|
-- DISTRO_HOME_URL=`https://www.ubuntu.com/`
|
||||||
|
-- DISTRO_ID=`ubuntu`
|
||||||
|
-- DISTRO_ID_LIKE=`debian`
|
||||||
|
-- DISTRO_NAME=`Ubuntu`
|
||||||
|
-- DISTRO_PRETTY_NAME=`Ubuntu 20.04.2 LTS`
|
||||||
|
-- DISTRO_PRIVACY_POLICY_URL=`https://www.ubuntu.com/legal/terms-and-policies/privacy-policy`
|
||||||
|
-- DISTRO_SUPPORT_URL=`https://help.ubuntu.com/`
|
||||||
|
-- DISTRO_UBUNTU_CODENAME=`focal`
|
||||||
|
-- DISTRO_VERSION=`20.04.2 LTS (Focal Fossa)`
|
||||||
|
-- DISTRO_VERSION_CODENAME=`focal`
|
||||||
|
-- DISTRO_VERSION_ID=`20.04`
|
||||||
|
|
||||||
.. rubric:: Footnotes
|
.. rubric:: Footnotes
|
||||||
|
|
||||||
.. [#mebibytes] One MiB (mebibyte) is equal to 1024x1024 bytes.
|
.. [#mebibytes] One MiB (mebibyte) is equal to 1024x1024 bytes.
|
||||||
|
|
||||||
|
.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||||
|
6
Help/release/dev/os-release.rst
Normal file
6
Help/release/dev/os-release.rst
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
os-release
|
||||||
|
----------
|
||||||
|
|
||||||
|
* The :command:`cmake_host_system_information` command query operating system
|
||||||
|
identification `variables <https://www.freedesktop.org/software/systemd/man/os-release.html>`_
|
||||||
|
from the :file:`/etc/os-release` file.
|
@ -2,28 +2,37 @@
|
|||||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||||
#include "cmCMakeHostSystemInformationCommand.h"
|
#include "cmCMakeHostSystemInformationCommand.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cassert>
|
||||||
|
#include <cctype>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <cm/optional>
|
#include <cm/optional>
|
||||||
#include <cm/string_view>
|
#include <cm/string_view>
|
||||||
#include <cmext/string_view>
|
#include <cmext/string_view>
|
||||||
|
|
||||||
|
#include "cmsys/FStream.hxx"
|
||||||
#include "cmsys/SystemInformation.hxx"
|
#include "cmsys/SystemInformation.hxx"
|
||||||
|
|
||||||
#include "cmExecutionStatus.h"
|
#include "cmExecutionStatus.h"
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
|
#include "cmStringAlgorithms.h"
|
||||||
|
#include "cmSystemTools.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include "cmAlgorithms.h"
|
# include "cmAlgorithms.h"
|
||||||
# include "cmGlobalGenerator.h"
|
# include "cmGlobalGenerator.h"
|
||||||
# include "cmGlobalVisualStudioVersionedGenerator.h"
|
# include "cmGlobalVisualStudioVersionedGenerator.h"
|
||||||
# include "cmStringAlgorithms.h"
|
|
||||||
# include "cmSystemTools.h"
|
|
||||||
# include "cmVSSetupHelper.h"
|
# include "cmVSSetupHelper.h"
|
||||||
# define HAVE_VS_SETUP_HELPER
|
# define HAVE_VS_SETUP_HELPER
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
std::string const DELIM[2] = { {}, ";" };
|
||||||
|
|
||||||
// BEGIN Private functions
|
// BEGIN Private functions
|
||||||
std::string ValueToString(std::size_t const value)
|
std::string ValueToString(std::size_t const value)
|
||||||
{
|
{
|
||||||
@ -138,6 +147,177 @@ cm::optional<std::string> GetValue(cmsys::SystemInformation& info,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine(
|
||||||
|
std::string const& line)
|
||||||
|
{
|
||||||
|
std::string key;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
char prev = 0;
|
||||||
|
enum ParserState
|
||||||
|
{
|
||||||
|
PARSE_KEY_1ST,
|
||||||
|
PARSE_KEY,
|
||||||
|
FOUND_EQ,
|
||||||
|
PARSE_SINGLE_QUOTE_VALUE,
|
||||||
|
PARSE_DBL_QUOTE_VALUE,
|
||||||
|
PARSE_VALUE,
|
||||||
|
IGNORE_REST
|
||||||
|
} state = PARSE_KEY_1ST;
|
||||||
|
|
||||||
|
for (auto ch : line) {
|
||||||
|
switch (state) {
|
||||||
|
case PARSE_KEY_1ST:
|
||||||
|
if (std::isalpha(ch) || ch == '_') {
|
||||||
|
key += ch;
|
||||||
|
state = PARSE_KEY;
|
||||||
|
} else if (!std::isspace(ch)) {
|
||||||
|
state = IGNORE_REST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSE_KEY:
|
||||||
|
if (ch == '=') {
|
||||||
|
state = FOUND_EQ;
|
||||||
|
} else if (std::isalnum(ch) || ch == '_') {
|
||||||
|
key += ch;
|
||||||
|
} else {
|
||||||
|
state = IGNORE_REST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FOUND_EQ:
|
||||||
|
switch (ch) {
|
||||||
|
case '\'':
|
||||||
|
state = PARSE_SINGLE_QUOTE_VALUE;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
state = PARSE_DBL_QUOTE_VALUE;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
case '\\':
|
||||||
|
state = IGNORE_REST;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value += ch;
|
||||||
|
state = PARSE_VALUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSE_SINGLE_QUOTE_VALUE:
|
||||||
|
if (ch == '\'') {
|
||||||
|
if (prev != '\\') {
|
||||||
|
state = IGNORE_REST;
|
||||||
|
} else {
|
||||||
|
assert(!value.empty());
|
||||||
|
value[value.size() - 1] = ch;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSE_DBL_QUOTE_VALUE:
|
||||||
|
if (ch == '"') {
|
||||||
|
if (prev != '\\') {
|
||||||
|
state = IGNORE_REST;
|
||||||
|
} else {
|
||||||
|
assert(!value.empty());
|
||||||
|
value[value.size() - 1] = ch;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSE_VALUE:
|
||||||
|
if (ch == '#' || std::isspace(ch)) {
|
||||||
|
state = IGNORE_REST;
|
||||||
|
} else {
|
||||||
|
value += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Unexpected os-release parser state!
|
||||||
|
state = IGNORE_REST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == IGNORE_REST) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = ch;
|
||||||
|
}
|
||||||
|
if (!(key.empty() || value.empty())) {
|
||||||
|
return std::make_pair(key, value);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> GetOSReleaseVariables(
|
||||||
|
cmExecutionStatus& status)
|
||||||
|
{
|
||||||
|
const auto& sysroot =
|
||||||
|
status.GetMakefile().GetSafeDefinition("CMAKE_SYSROOT");
|
||||||
|
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
// Based on
|
||||||
|
// https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||||
|
for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) {
|
||||||
|
const auto& filename = cmStrCat(sysroot, name);
|
||||||
|
if (cmSystemTools::FileExists(filename)) {
|
||||||
|
cmsys::ifstream fin(filename.c_str());
|
||||||
|
for (std::string line; !std::getline(fin, line).fail();) {
|
||||||
|
auto kv = ParseOSReleaseLine(line);
|
||||||
|
if (kv.has_value()) {
|
||||||
|
data.emplace(kv.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm::optional<std::string> GetValue(cmExecutionStatus& status,
|
||||||
|
std::string const& key,
|
||||||
|
std::string const& variable)
|
||||||
|
{
|
||||||
|
const auto prefix = "DISTRIB_"_s;
|
||||||
|
if (!cmHasPrefix(key, prefix)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::map<std::string, std::string> s_os_release =
|
||||||
|
GetOSReleaseVariables(status);
|
||||||
|
|
||||||
|
auto& makefile = status.GetMakefile();
|
||||||
|
|
||||||
|
const std::string subkey =
|
||||||
|
key.substr(prefix.size(), key.size() - prefix.size());
|
||||||
|
if (subkey == "INFO"_s) {
|
||||||
|
std::string vars;
|
||||||
|
for (const auto& kv : s_os_release) {
|
||||||
|
auto cmake_var_name = cmStrCat(variable, '_', kv.first);
|
||||||
|
vars += DELIM[!vars.empty()] + cmake_var_name;
|
||||||
|
makefile.AddDefinition(cmake_var_name, kv.second);
|
||||||
|
}
|
||||||
|
return cm::optional<std::string>(std::move(vars));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query individual variable
|
||||||
|
const auto it = s_os_release.find(subkey);
|
||||||
|
if (it != s_os_release.cend()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE Empty string means requested variable not set
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_VS_SETUP_HELPER
|
#ifdef HAVE_VS_SETUP_HELPER
|
||||||
cm::optional<std::string> GetValue(cmExecutionStatus& status,
|
cm::optional<std::string> GetValue(cmExecutionStatus& status,
|
||||||
std::string const& key)
|
std::string const& key)
|
||||||
@ -201,10 +381,9 @@ bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
|
|||||||
|
|
||||||
std::string result_list;
|
std::string result_list;
|
||||||
for (auto i = current_index + 1; i < args.size(); ++i) {
|
for (auto i = current_index + 1; i < args.size(); ++i) {
|
||||||
|
result_list += DELIM[!result_list.empty()];
|
||||||
|
|
||||||
auto const& key = args[i];
|
auto const& key = args[i];
|
||||||
if (i != current_index + 1) {
|
|
||||||
result_list += ";";
|
|
||||||
}
|
|
||||||
auto value = GetValue(info, key);
|
auto value = GetValue(info, key);
|
||||||
if (!value) {
|
if (!value) {
|
||||||
#ifdef HAVE_VS_SETUP_HELPER
|
#ifdef HAVE_VS_SETUP_HELPER
|
||||||
@ -213,6 +392,12 @@ bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
|
|||||||
status.SetError("does not recognize <key> " + key);
|
status.SetError("does not recognize <key> " + key);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
value = GetValue(status, key, variable);
|
||||||
|
if (!value) {
|
||||||
|
status.SetError("does not recognize <key> " + key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
status.SetError("does not recognize <key> " + key);
|
status.SetError("does not recognize <key> " + key);
|
||||||
return false;
|
return false;
|
||||||
|
@ -349,7 +349,7 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE)
|
|||||||
endif()
|
endif()
|
||||||
add_RunCMake_test(execute_process)
|
add_RunCMake_test(execute_process)
|
||||||
add_RunCMake_test(export)
|
add_RunCMake_test(export)
|
||||||
add_RunCMake_test(cmake_host_system_information)
|
add_RunCMake_test(cmake_host_system_information -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
|
||||||
add_RunCMake_test(cmake_language)
|
add_RunCMake_test(cmake_language)
|
||||||
add_RunCMake_test(cmake_minimum_required)
|
add_RunCMake_test(cmake_minimum_required)
|
||||||
add_RunCMake_test(cmake_parse_arguments)
|
add_RunCMake_test(cmake_parse_arguments)
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
-- TEST1_ANSI_COLOR=`0;32`
|
||||||
|
-- TEST1_BUG_REPORT_URL=`https://bugs.exherbo.org/`
|
||||||
|
-- TEST1_HOME_URL=`https://www.exherbo.org/`
|
||||||
|
-- TEST1_ID=`exherbo`
|
||||||
|
-- TEST1_NAME=`Exherbo`
|
||||||
|
-- TEST1_PRETTY_NAME=`Exherbo Linux`
|
||||||
|
-- TEST1_SUPPORT_URL=`irc://irc.freenode.net/#exherbo`
|
||||||
|
-- TEST2_ID=`exherbo`
|
||||||
|
-- TEST2_VERSION=``
|
11
Tests/RunCMake/cmake_host_system_information/Exherbo.cmake
Normal file
11
Tests/RunCMake/cmake_host_system_information/Exherbo.cmake
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
cmake_host_system_information(RESULT TEST1 QUERY DISTRIB_INFO)
|
||||||
|
|
||||||
|
foreach(VAR IN LISTS TEST1)
|
||||||
|
message(STATUS "${VAR}=`${${VAR}}`")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Query individual variables
|
||||||
|
cmake_host_system_information(RESULT TEST2 QUERY DISTRIB_ID DISTRIB_VERSION)
|
||||||
|
list(POP_FRONT TEST2 TEST2_ID TEST2_VERSION)
|
||||||
|
message(STATUS "TEST2_ID=`${TEST2_ID}`")
|
||||||
|
message(STATUS "TEST2_VERSION=`${TEST2_VERSION}`")
|
@ -0,0 +1,7 @@
|
|||||||
|
NAME="Exherbo"
|
||||||
|
PRETTY_NAME="Exherbo Linux"
|
||||||
|
ID="exherbo"
|
||||||
|
ANSI_COLOR="0;32"
|
||||||
|
HOME_URL="https://www.exherbo.org/"
|
||||||
|
SUPPORT_URL="irc://irc.freenode.net/#exherbo"
|
||||||
|
BUG_REPORT_URL="https://bugs.exherbo.org/"
|
@ -6,3 +6,11 @@ run_cmake(BadArg3)
|
|||||||
|
|
||||||
run_cmake(QueryList)
|
run_cmake(QueryList)
|
||||||
run_cmake(QueryKeys)
|
run_cmake(QueryKeys)
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
|
||||||
|
run_cmake_with_options(UnitTest)
|
||||||
|
run_cmake_with_options(Exherbo)
|
||||||
|
run_cmake_with_options(Ubuntu)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
-- TEST1_BUG_REPORT_URL=`https://bugs\.launchpad\.net/ubuntu/`
|
||||||
|
-- TEST1_HOME_URL=`https://www\.ubuntu\.com/`
|
||||||
|
-- TEST1_ID=`ubuntu`
|
||||||
|
-- TEST1_ID_LIKE=`debian`
|
||||||
|
-- TEST1_NAME=`Ubuntu`
|
||||||
|
-- TEST1_PRETTY_NAME=`Ubuntu 20\.04\.2 LTS`
|
||||||
|
-- TEST1_PRIVACY_POLICY_URL=`https://www\.ubuntu\.com/legal/terms-and-policies/privacy-policy`
|
||||||
|
-- TEST1_SUPPORT_URL=`https://help\.ubuntu\.com/`
|
||||||
|
-- TEST1_UBUNTU_CODENAME=`focal`
|
||||||
|
-- TEST1_VERSION=`20\.04\.2 LTS \(Focal Fossa\)`
|
||||||
|
-- TEST1_VERSION_CODENAME=`focal`
|
||||||
|
-- TEST1_VERSION_ID=`20\.04`
|
||||||
|
-- TEST2_ID=`ubuntu`
|
||||||
|
-- TEST2_VERSION=`20\.04\.2 LTS \(Focal Fossa\)`
|
11
Tests/RunCMake/cmake_host_system_information/Ubuntu.cmake
Normal file
11
Tests/RunCMake/cmake_host_system_information/Ubuntu.cmake
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
cmake_host_system_information(RESULT TEST1 QUERY DISTRIB_INFO)
|
||||||
|
|
||||||
|
foreach(VAR IN LISTS TEST1)
|
||||||
|
message(STATUS "${VAR}=`${${VAR}}`")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Query individual variables
|
||||||
|
cmake_host_system_information(RESULT TEST2 QUERY DISTRIB_ID DISTRIB_VERSION)
|
||||||
|
list(POP_FRONT TEST2 TEST2_ID TEST2_VERSION)
|
||||||
|
message(STATUS "TEST2_ID=`${TEST2_ID}`")
|
||||||
|
message(STATUS "TEST2_VERSION=`${TEST2_VERSION}`")
|
@ -0,0 +1,12 @@
|
|||||||
|
NAME="Ubuntu"
|
||||||
|
VERSION="20.04.2 LTS (Focal Fossa)"
|
||||||
|
ID=ubuntu
|
||||||
|
ID_LIKE=debian
|
||||||
|
PRETTY_NAME="Ubuntu 20.04.2 LTS"
|
||||||
|
VERSION_ID="20.04"
|
||||||
|
HOME_URL="https://www.ubuntu.com/"
|
||||||
|
SUPPORT_URL="https://help.ubuntu.com/"
|
||||||
|
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||||
|
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||||
|
VERSION_CODENAME=focal
|
||||||
|
UBUNTU_CODENAME=focal
|
@ -0,0 +1,7 @@
|
|||||||
|
-- UNIT_TEST_A_LIST_LIKE_VARIABLE=`satu;dua;tiga`
|
||||||
|
-- UNIT_TEST_DBL_QUOTED_VALUE=`"The" value in double "quotes"`
|
||||||
|
-- UNIT_TEST_DBL_QUOTED_VALUE_STIPPED_COMMENT=`Blah blah blah`
|
||||||
|
-- UNIT_TEST_NON_SPACE_VALUE=`Blah-blah-blah`
|
||||||
|
-- UNIT_TEST_QUOTED_VALUE=`'The' value in single 'quotes'`
|
||||||
|
-- UNIT_TEST_QUOTED_VALUE_STIPPED_COMMENT=`The value in single quotes`
|
||||||
|
-- UNIT_TEST_THE_URL_WITH_ANCHOR_TEST=`https://blah.blah/resource#anchor`
|
@ -0,0 +1,5 @@
|
|||||||
|
cmake_host_system_information(RESULT UNIT_TEST QUERY DISTRIB_INFO)
|
||||||
|
|
||||||
|
foreach(VAR IN LISTS UNIT_TEST)
|
||||||
|
message(STATUS "${VAR}=`${${VAR}}`")
|
||||||
|
endforeach()
|
@ -0,0 +1,9 @@
|
|||||||
|
# Comment string gonna be ignored
|
||||||
|
NON_SPACE_VALUE=Blah-blah-blah
|
||||||
|
QUOTED_VALUE='\'The\' value in single \'quotes\''
|
||||||
|
QUOTED_VALUE_STIPPED_COMMENT='The value in single quotes'# The comment right after `'`
|
||||||
|
DBL_QUOTED_VALUE="\"The\" value in double \"quotes\""
|
||||||
|
DBL_QUOTED_VALUE_STIPPED_COMMENT="Blah blah blah"# The comment right after `'`
|
||||||
|
THE_URL_WITH_ANCHOR_TEST="https://blah.blah/resource#anchor" # And a comment after
|
||||||
|
A_LIST_LIKE_VARIABLE='satu;dua;tiga'
|
||||||
|
INCORRECT_ESCAPE_IGNORED=\'This line gonna be ignored'
|
Loading…
Reference in New Issue
Block a user