Make error codes

Make return error code After each shell invocation returns, make looks at its exit status. If the shell completed successfully (the exit status is zero), the next line in the recipe is executed in a new shell; after the last line is finished, the rule is finished. If there is an error (the exit […]

Содержание

  1. Make return error code
  2. Where can I find a list of ‘make’ error codes?
  3. 2 Answers 2
  4. Return your errors instead of throwing them in typescript
  5. You don’t need a library
  6. Use the error-return pattern for external libraries & stdlib
  7. Wrap unreliable functions to make them error-returning
  8. Conclusion
  9. Поддержка системных ошибок в C++
  10. Предисловие
  11. Часть 1
  12. Введение
  13. Где взять?
  14. Краткий обзор
  15. Принципы
  16. Не все ошибки являются исключительными
  17. Ошибки исходят из нескольких источников
  18. Возможность пользовательского расширения
  19. Сохранение изначального кода ошибки
  20. Часть 2
  21. error_code vs error_condition
  22. Важно то, что вы будете с этим делать
  23. Сравнение error_code и error_condition
  24. Как узнать, какие условия вы можете проверить?
  25. Немного истории
  26. Часть 3
  27. Перечисляемые значения как константы класса
  28. Это не совсем так просто
  29. Шаг 1: определить, является ли перечисляемое значение кодом ошибки или же условием
  30. Шаг 2: сопоставить значение ошибки с категорией ошибки
  31. Явное преобразование в error_code или error_condition
  32. Еще немного истории
  33. Часть 4
  34. Создание собственных кодов ошибок
  35. Шаг 1: определить значения ошибок
  36. Шаг 2: определить класс error_category
  37. Шаг 3: дать категории человеко-читаемое имя
  38. Шаг 4: конвертировать коды ошибок в строки
  39. Шаг 5: уникальная идентификация категории
  40. Шаг 6: построить error_code из enum
  41. Шаг 7: запись для неявного преобразования в error_code
  42. Шаг 8 (опциональный): определить условия ошибок по умолчанию
  43. Использование кодов ошибок
  44. Часть 5
  45. Создание собственных условий ошибок
  46. Зачем создавать свои условия ошибок?
  47. Шаг 1: определить значения ошибок
  48. Шаг 2: определить класс error_category
  49. Шаг 3: дать категории человеко-читаемое имя
  50. Шаг 4: конвертировать условия ошибок в строки
  51. Шаг 5: реализовать эквивалентность ошибок
  52. Шаг 6: уникальная идентификация категории
  53. Шаг 7: построить error_condition из enum
  54. Шаг 8: запись для неявного преобразования в error_condition
  55. Использование условий ошибок

Make return error code

After each shell invocation returns, make looks at its exit status. If the shell completed successfully (the exit status is zero), the next line in the recipe is executed in a new shell; after the last line is finished, the rule is finished.

If there is an error (the exit status is nonzero), make gives up on the current rule, and perhaps on all rules.

Sometimes the failure of a certain recipe line does not indicate a problem. For example, you may use the mkdir command to ensure that a directory exists. If the directory already exists, mkdir will report an error, but you probably want make to continue regardless.

To ignore errors in a recipe line, write a ‘ — ’ at the beginning of the line’s text (after the initial tab). The ‘ — ’ is discarded before the line is passed to the shell for execution.

This causes make to continue even if rm is unable to remove a file.

When you run make with the ‘ -i ’ or ‘ —ignore-errors ’ flag, errors are ignored in all recipes of all rules. A rule in the makefile for the special target .IGNORE has the same effect, if there are no prerequisites. This is less flexible but sometimes useful.

When errors are to be ignored, because of either a ‘ — ’ or the ‘ -i ’ flag, make treats an error return just like success, except that it prints out a message that tells you the status code the shell exited with, and says that the error has been ignored.

When an error happens that make has not been told to ignore, it implies that the current target cannot be correctly remade, and neither can any other that depends on it either directly or indirectly. No further recipes will be executed for these targets, since their preconditions have not been achieved.

Normally make gives up immediately in this circumstance, returning a nonzero status. However, if the ‘ -k ’ or ‘ —keep-going ’ flag is specified, make continues to consider the other prerequisites of the pending targets, remaking them if necessary, before it gives up and returns nonzero status. For example, after an error in compiling one object file, ‘ make -k ’ will continue compiling other object files even though it already knows that linking them will be impossible. See Summary of Options.

The usual behavior assumes that your purpose is to get the specified targets up to date; once make learns that this is impossible, it might as well report the failure immediately. The ‘ -k ’ option says that the real purpose is to test as many of the changes made in the program as possible, perhaps to find several independent problems so that you can correct them all before the next attempt to compile. This is why Emacs’ compile command passes the ‘ -k ’ flag by default.

Usually when a recipe line fails, if it has changed the target file at all, the file is corrupted and cannot be used—or at least it is not completely updated. Yet the file’s time stamp says that it is now up to date, so the next time make runs, it will not try to update that file. The situation is just the same as when the shell is killed by a signal; see Interrupts. So generally the right thing to do is to delete the target file if the recipe fails after beginning to change the file. make will do this if .DELETE_ON_ERROR appears as a target. This is almost always what you want make to do, but it is not historical practice; so for compatibility, you must explicitly request it.

Источник

Where can I find a list of ‘make’ error codes?

I am trying to compile a program written in Fortran using make (I have a Makefile and, while in the directory containing the Makefile, I type the command $ make target , where «target» is a system-specific target specification is present in my Makefile. As I experiment with various revisions of my target specification, I often get a variety of error messages when attempting to call make . To give a few examples:

Do you know how I can find a list of what the error codes, such as «Error 126» and «Error 2,» mean? I found this thread on another website, but I am not sure what the reply means. Does it mean that there is no system-independent meaning of the make error codes? Can you please help me? Thank you.

2 Answers 2

The error codes aren’t from make: make is reporting the return status of the command that failed. You need to look at the documentation of each command to know what each status value means. Most commands don’t bother with distinctions other than 0 = success, anything else = failure.

In each of your examples, ./dpp cannot be executed. When this happens, the shell that tried to invoke it exits with status code 126 (this is standard behavior). The instance of make that was running that shell detects a failed command (the shell) and exits, showing you Error 126 . That instance of make is itself a command executed by a parent instance of make, and the make utility returns 2 on error, so the parent make reports Error 2 .

The failure of your build is likely to stem from test: too many arguments . This could be a syntax error in the makefile, or it could be due to relying on bash-specific features when you have a /bin/sh that isn’t bash. Try running make SHELL=/bin/bash target or make SHELL=/bin/ksh target ; if that doesn’t work, you need to fix your makefile.

Источник

Return your errors instead of throwing them in typescript

Want your code to never throw errors during runtime? You can get pretty close using this error-return pattern inspired by golang. (I assume Go copied it from a long tradition.)

The problem: Typescript and javascript have no way to indicate that a function may throw an error. So you typically have to either run your code until it crashes or hunt down the source code in github or node_modules (since the bundled dist usually only has non-minified headers) to figure out where to try/catch.

The error-return pattern makes your tooling track where errors may occur so you don’t have to memorize or hunt for that information yourself. With error-return, an unhandled error is immediately shown in your editor with a typescript error.

Here’s an example of the pattern in a sequential, branching networking task:

Exit fullscreen mode

(Quite nice compared to four levels of indentation with try-catch.)

Then, if you tried to use this function without catching the error in, say, an html element you would get a type error:

Exit fullscreen mode

The pattern forces you to account for the failure case:

Exit fullscreen mode

Useful for preventing:

  • blank screen and «button does nothing» bugs in the browser
  • server timeout and bad response bugs in node
  • system scripts failing in intermediate states, leaving junk behind
  • unexpected errors in library code

You don’t need a library

All the code for this pattern fits in a short file, and you can customize it to your needs. Here’s my implementation:

Exit fullscreen mode

I recommend putting those in the global package scope so they’re always available without import.

Use the error-return pattern for external libraries & stdlib

Easiest to demonstrate with an example

Exit fullscreen mode

Wrap unreliable functions to make them error-returning

If you’re using some library functions all over the place and are tired of repeating the tryFail(()=>. ) everywhere (even though it beats massive try-catch chains), it can be helpful to wrap the library with error-returning logic.

We just need one more function in our error library:

Exit fullscreen mode

Then we can use it to make wrapped library:

Exit fullscreen mode

Then you can use the library code with perfect elegance and reliability

Exit fullscreen mode

A little inconvenience, but it’s just two lines to wrap any function f . Worth the effort if you’re using f more than a few times.

Conclusion

In short, this simple pattern can make typescript code dramatically more reliable while avoiding the awkward empty let s with nested try/catch that pervade typescript networking code.

Источник

Поддержка системных ошибок в C++

Предисловие

Я долго думал, нужно ли делать перевод этого, уже известного, цикла статей под названием «System error support in C++0x», повествующего о и обработке ошибок. С одной стороны он написан в 2010 году и меня попросту могут счесть некрофилом, а с другой стороны в рунете очень мало информации по этой теме и многие довольно свежие статьи ссылаются на этот цикл, что говорит о том, что он не теряет актуальности и по сей день.

Потому я решил, что увековечить сей труд в граните на Хабре будет неплохой идеей.

Сразу хочу предупредить, что опыта переводчика у меня нет и вообще май инглиш из бед. И огорчений. Так что буду рад вашей критике и предложениям, желательно в личку.

Часть 1

Введение

Среди новых функций стандартной библиотеки в C++0x есть небольшой заголовочный файл под названием . Он предоставляет набор инструментов для управления, внезапно, системными ошибками.

Основными компонентами, определенными в нем, являются:

  • class error_category
  • class error_code
  • class error_condition
  • class system_error
  • enum class errc

Я приложил руку к дизайну этого модуля, так что в серии своих статей я постараюсь рассказать о причинах появления, истории и предполагаемом использовании его компонентов.

Где взять?

Полная реализация, а также поддержка C++03, включена в Boost. Я предполагаю, что на данный момент это, вероятно, лучшая проверенная реализация с точки зрения переносимости. Разумеется, вы должны писать boost::system:: , а не std:: .

Реализация включена в GCC 4.4 и более поздних версиях. Однако вы должны скомпилировать свою программу с опцией -std=c++0x, чтобы ее использовать.

Наконец, Microsoft Visual Studio 2010 будет поставляться с реализацией данных классов [но с ограничениями]. Основное ограничение заключается в том, что system_category() не представляет ошибки Win32 так как это было задумано. Подробнее о том, что это значит будет сказано позже.

(Обратите внимание, что это только те реализации, о которых я знаю. Могут быть и другие).

[Примечание переводчика: конечно же, эта информация уже давно устарела, теперь является неотъемлемой частью современной стандартной библиотеки]

Краткий обзор

Ниже приведены типы и классы, определенные в , в двух словах:

  • class error_category — базовый класс, используется для определения источников ошибок, а так же категорий кодов ошибок и условий.
  • сlass error_code — представляет собой конкретное значение ошибки, возвращаемое операцией (например, системным вызовом).
  • class error_condition — что-то, что вы хотите проверить и, возможно, среагировать на это в своем коде.
  • class system_error — исключение, используемое для обертывания error_code , когда ошибка будет передана с помощью throw/catch.
  • enum class errc — набор общих условий ошибок, унаследованный от POSIX.
  • is_error_code_enum<>, is_error_condition_enum<>, make_error_code, make_error_condition — механизм преобразования перечислений с значениями ошибок в error_code или error_condition .
  • generic_category() — возвращает объект категории, используемый для классификации кодов ошибок и условий основанных на errc .
  • system_category() — возвращает объект категории, используемый для [классификации] кодов ошибок исходящих от операционной системы.

Принципы

В этом разделе перечислены некоторые основные принципы, которых я придерживался при проектировании модуля. (Я не могу говорить за остальных участников). Как и в большинстве программных проектов, некоторые из них были целями с самого начала, а некоторые возникли в процессе.

Не все ошибки являются исключительными

Проще говоря, исключения — это не всегда правильный способ обработки ошибок. (В некоторых кругах это утверждение является противоречивым, хотя я действительно не понимаю, почему.)

Например, в сетевом программировании обычно встречаются такие ошибки, как:

  • Вы не смогли подключиться к удаленному IP-адресу.
  • Ваше соединение упало.
  • Вы попытались открыть IPv6-сокет, но у вас нет доступных сетевых IPv6-интерфейсов.

Конечно, они могут быть исключительными обстоятельствами, но в равной степени они могут обрабатываться и как часть нормального потока управления. Если вы разумно ожидаете, что это произойдет, это не является исключительным. Соответственно:

  • IP-адрес является лишь одним из списка адресов, соответствующих имени узла. Вы хотите попробовать подключиться к следующему адресу в списке.
  • Сеть ненадежна. Вы хотите попытаться восстановить соединение и сдаться только после N сбоев.
  • Ваша программа может вернуться к использованию IPv4-сокета.

Другим требованием, в случае asio, был способ передать результат асинхронной операции обработчику завершения. В этом случае я хочу, чтобы код ошибки был аргументом для обработчика обратного вызова.

(Альтернативный подход заключается в том, чтобы предоставить средство для реконструирования исключения внутри обработчика, такого как асинхронный шаблон .NET BeginXYZ/EndXYZ . На мой взгляд, такой дизайн добавляет сложности и делает API более подверженным ошибкам.)

[Примечание переводчика: теперь таким средством может быть std::exception_ptr из C++11]

И последнее, но не менее важное: в некоторых областях нельзя использовать исключения из-за размера кода и ограничений производительности.

В общем, нужно быть прагматичным, а не догматичным. Использовать любой механизм ошибок лучше всего с точки зрения ясности, правильности, ограничений и, да, даже личного вкуса. Часто правильным критерием для принятия решения между исключением и кодом ошибки является способ использования. Это означает, что представление системной ошибки должно поддерживать оба [варианта].

Ошибки исходят из нескольких источников

Стандарт C++03 распознает errno как источник кодов ошибок. Это используется функциями stdio, некоторыми математическими функциями и так далее.

На POSIX платформах многие системные операции используют errno для передачи ошибок. POSIX определяет дополнительные коды ошибок errno для покрытия этих случаев.

Windows, с другой стороны, не использует errno за пределами стандартной библиотеки C. Вызовы Windows API обычно сообщают об ошибках через GetLastError() .

Если рассматривать сетевое программирование, семейство функций getaddrinfo использует собственный набор кодов ошибок (EAI_. ) на POSIX, но разделяет «пространство имен» GetLastError() в Windows. Программы, интегрирующие другие библиотеки (для SSL, регулярных выражений и так далее), столкнутся с другими категориями кодов ошибок.

Программы должны иметь возможность управлять этими кодами ошибок согласованным образом. Я особенно заинтересован тем [способом], что позволит сочетать операции для создания абстракций более высокого уровня. Объединение системных вызовов, getaddrinfo , SSL и регулярных выражений в один API не должно заставлять пользователя этого API бороться со «взрывом» типов кодов ошибок. Добавление нового источника ошибок в реализацию этого API не должно изменять его интерфейс.

Возможность пользовательского расширения

Пользователи стандартной библиотеки должны иметь возможность добавлять свои собственные источники ошибок. Эта возможность может быть использована просто для интеграции сторонней библиотеки, но также она связана с желанием создавать абстракции более высокого уровня. При разработке реализации протокола, такого как HTTP, я хочу иметь возможность добавлять набор кодов ошибок, соответствующих ошибкам, определенным в RFC.

Сохранение изначального кода ошибки

Это не было одной из моих первоначальных целей: я думал, что стандарт предоставит набор хорошо известных кодов ошибок. Если системная операция вернула ошибку, библиотека должна была перевести ошибку в хорошо известный код (если такое отображение имело смысл).

К счастью, кто-то указал мне на проблему моего подхода. Перевод кода ошибки сбрасывает информацию: ошибка, возвращаемая основным системным вызовом, теряется. Это может быть не очень важно с точки зрения потока управления программой, но это имеет большое значение для поддержки программы. Нет никаких сомнений в том, что программисты будут использовать стандартизованный код ошибки для логирования и трассировки, а изначальная ошибка может быть жизненно важна для диагностики проблем.

Этот окончательный принцип прекрасно сочетается с моей темой для второй части: error_code vs error_condition . Будьте на связи.

Часть 2

error_code vs error_condition

Из 1000+ страниц стандарта C++0x случайный читатель должен подметить одну вещь: error_code и error_condition выглядят практически идентичными! Что происходит? Это последствия бездумной копипасты?

Важно то, что вы будете с этим делать

Давайте посмотрим на описания, которые я давал в первой части, еще раз:

  • сlass error_code — представляет собой конкретное значение ошибки, возвращаемое операцией (например, системным вызовом).
  • class error_condition — что-то, что вы хотите проверить и, возможно, среагировать на это в своем коде.

Классы различаются, потому что они предназначены для разных целей. В качестве примера рассмотрим гипотетическую функцию под названием create_directory() :

Которую вы вызываете следующим образом:

Операция может завершиться неудачей по разным причинам, например:

  • Нет нужных прав.
  • Каталог уже существует.
  • Путь слишком длинный.
  • Родительский путь не существует.

Какова бы ни была причина сбоя, когда функция create_directory() вернет управление переменная ec будет содержать код ошибки, специфичный для ОС. С другой стороны, если вызов был успешным, то в ec будет находиться нулевое значение. Это дань традиции (используемой errno и GetLastError() ), когда ноль указывает на успех, а любые другие значения на ошибку.

Если вас интересует только была ли операция успешной или неудачной, вы можете использовать тот факт, что error_code легко конвертируется в bool :

Однако предположим, что вы заинтересованы в проверке ошибки «каталог уже существует». Если случится эта ошибка, то наша гипотетическая программа может продолжать работать. Давайте попробуем реализовать это:

Этот код неправильный. Он может заработать на POSIX платформах, но не забывайте, что ec будет содержать платформозависимую ошибку. В Windows ошибка, скорее всего, будет ERROR_ALREADY_EXISTS . (Хуже того, код не проверяет категорию кода ошибки, но мы поговорим об этом позже.)

Практическое правило: если вы вызываете error_code::value() , то вы делаете что-то не так.

Итак у вас есть платформозависимый код ошибки ( EEXIST или ERROR_ALREADY_EXISTS ), который вы хотите сопоставить с [платформонезависимым] условием ошибки («каталог уже существует»). Да, правильно, вам нужен error_condition .

Сравнение error_code и error_condition

Вот что происходит при сравнении объектов error_code и error_condition (то есть при использовании оператора == или оператора !=):

  • error_code и error_code — проверяется точное соответствие.
  • error_condition и error_condition — проверяется точное соответствие.
  • error_code и error_condition — проверяется эквивалентность.

Я надеюсь, теперь очевидно, что вы должны сравнивать свой платформозависимый код ошибки ec с объектом error_condition , который представляет ошибку «каталог уже существует». Как раз для такого случая C++0x предоставляет std::errc::file_exists . Это означает, что вы должны писать:

Это работает потому что разработчик стандартной библиотеки определил эквивалентность между кодами ошибок EEXIST или ERROR_ALREADY_EXISTS и условием ошибки std::errc::file_exists . Позже я покажу как вы можете добавить свои собственные коды ошибок и условия с соответствующими определениями эквивалентности.

(Обратите внимание, что, если быть точным, std::errc::file_exists это одно из перечисляемых значений из enum class errc . Пока что вы должны думать о перечисляемых значениях std::errc::* как о метках для констант error_condition . В следующей части я объясню как это работает.)

Как узнать, какие условия вы можете проверить?

Некоторые из новых библиотечных функций в C++0x имеют раздел «Условия ошибок». В подобных разделах перечисляются константы error_condition и условия, при которых генерируются эквивалентные коды ошибок.

Немного истории

Первоначальный класс error_code был предложен для TR2 как вспомогательный компонент в библиотеках файловой системы и сетевых библиотеках. В том дизайне константа error_code была реализована так, чтобы она, по возможности, соответствовала платформозависимой ошибке. Если соответствие невозможно или существует несколько соответствий, реализация библиотеки переводит платформозависимую ошибку в стандартный error_code .

В обсуждениях по электронной почте я узнал о ценности сохранения изначального кода ошибки. Впоследствии был прототипирован класс generic_error , но он меня не устраивал. Удовлетворительное решение было найдено при переименовании generic_error в error_condition . По моему опыту, именование — одна из самых сложных проблем в области компьютерных наук, и выбор хорошего имени является основной работой.

В следующей части мы рассмотрим механизм, который заставляет enum class errc работать как набор констант для error_condition .

Часть 3

Перечисляемые значения как константы класса

Как мы видели, заголовочный файл определяет class enum errc следующим образом:

Перечисляемые значения которого являются константами для error_condition :

Очевидно, здесь используется неявное преобразование из errc в error_condition с помощью конструктора с одним аргументом. Просто. Верно?

Это не совсем так просто

Есть несколько причин, из-за которых всё немного сложнее:

  • Перечисляемое значение указывает на саму ошибку, но для построения error_condition необходимо знать еще и категорию ошибки. Модуль использует категории для поддержки нескольких источников ошибок. Категория является атрибутом как для error_code , так и для error_condition .
  • Объект должен быть расширяемым. То есть пользователи (а также будущие расширения стандартной библиотеки) должны иметь возможность определять свои собственные константы.
  • Объект должен поддерживать константы и для error_code и для error_condition . Хотя enum class errc предоставляет константы только для error_condition , в других случаях использования могут потребоваться константы типа error_code .
  • Наконец, должно поддерживаться явное преобразование из перечисляемого значения в error_code или error_condition . Портируемым программам может потребоваться создание кодов ошибок, унаследованных от std::errc::* .

Итак, хотя верно, что строка:

неявно преобразуется из errc в error_condition , есть еще несколько шагов.

Шаг 1: определить, является ли перечисляемое значение кодом ошибки или же условием

Для регистрации типов перечислений используются два шаблона:

Если тип зарегистрирован с использованием is_error_code_enum<> , то он может быть неявно преобразован в error_code . Аналогично, если тип зарегистрирован с использованием is_error_condition_enum<> , он может быть неявно преобразован в error_condition . По умолчанию типы регистрируются без преобразования (отсюда и использование false_type выше), но enum class errc зарегистрирован следующим образом:

Неявное преобразование выполняется с помощью условно разрешенных конструкторов преобразования. Это, вероятно, реализовано с использованием SFINAE, но для простоты вам нужно думать об этом как:

Поэтому, когда мы пишем:

Компилятор выбирает между этими двумя перегрузками:

Он выберет последний, поскольку конструктор преобразования error_condition доступен, а error_code нет.

Шаг 2: сопоставить значение ошибки с категорией ошибки

Объект error_condition содержит два атрибута: значение и категорию. Теперь, когда мы добрались до конструктора, они должны быть правильно инициализированы.

Это достигается благодаря конструктору имеющему вызов функции make_error_condition() .
Возможность пользовательского расширение реализована с помощью ADL механизма. Конечно, поскольку errc расположен в пространстве имен std , ADL находит make_error_condition() там же.

Реализация make_error_condition() проста:

Как вы можете видеть, эта функция использует конструктор error_condition с двумя аргументами, чтобы явно указать как значение ошибки, так и категорию.

Если бы мы были в конструкторе преобразования error_code (для правильно зарегистрированного типа перечисления), вызываемая функция была бы make_error_code() . В остальном конструкция error_code и error_condition одинакова.

Явное преобразование в error_code или error_condition

Хотя error_code в первую очередь предназначен для использования с платформозависимыми ошибками, переносимый код может захотеть создать error_code из перечисляемого значения errc . По этой причине предусмотрены [функции] make_error_code(errc) и make_error_condition(errc) . Переносимый код может использовать их следующим образом:

Еще немного истории

Изначально в константы error_code были определены как объекты:

LWG была обеспокоена издержками из-за большого количества глобальных объектов и запросила альтернативное решение. Мы исследовали возможность использования constexpr , но в итоге это оказалось несовместимым с некоторыми другими аспектами . Таким образом осталось только преобразование из перечисления, так как это был лучший доступный дизайн.

Далее я начну показывать, каким образом вы можете добавить свои собственные коды ошибок и условия.

Часть 4

Создание собственных кодов ошибок

Как я уже сказал в первой части, одним из принципов является расширяемость. Это означает, что вы можете использовать только что описанный механизм, чтобы определить свои собственные коды ошибок.

В этом разделе я опишу, что вам нужно сделать. В качестве основы для рабочего примера предположим, что вы пишете HTTP библиотеку и вам нужны ошибки, соответствующее кодам состояния HTTP.

Шаг 1: определить значения ошибок

Сначала вам нужно определить набор значений ошибок. Если вы используете C++0x, вы можете использовать class enum , аналогичный std::errc :

Ошибкам присваиваются значения в соответствии с кодами состояния HTTP. Важность этого станет очевидной, когда дело дойдет до использования кодов ошибок. Независимо от того, какие значения вы выберете, ошибки должны иметь ненулевые значения. Как вы помните, объект использует соглашение, в котором нуль означает успех.

Вы можете использовать обычный (то есть C++03-совместимый) enum , отбросив ключевое слово class :

Примечание: class enum отличается от enum тем, что первый заключает имена перечисляемых значений в классовой области видимости [в то время как второй «выбрасывает» их в глобальную область видимости]. Чтобы получить доступ к перечисляемому значению, вы должны указать имя класса, например: http_error::ok . Вы можете эмулировать это поведение, обернув обычный enum в пространство имен [ namespace ]:

В оставшейся части этого примера я буду использовать enum class . Применение подхода, использующего пространство имен, остается в качестве упражнения для читателя.

[Примечание переводчика: на самом деле, они отличаются не только областью видимости — enum class так же запрещает неявное приведение перечисляемых значений к другим типам]

Шаг 2: определить класс error_category

Объект error_code состоит из значения ошибки и категории. Категория ошибки определяет, что конкретно означает данное перечисляемое значение. Например, 100 может означать как http_error::continue_request , так и std::errc::network_down (ENETDOWN в Linux), а может и что-то другое.

Чтобы создать новую категорию, нужно отнаследовать класс от error_category :

На данный момент этот класс будет реализовывать только чистые виртуальные функции error_category .

Шаг 3: дать категории человеко-читаемое имя

Виртуальная функция error_category::name() должна возвращать строку, идентифицирующую категорию:

Это имя не обязательно должно быть полностью уникальным, поскольку оно используется только при записи кода ошибки в std::ostream . Однако было бы желательно сделать его уникальным в рамках данной программы.

Шаг 4: конвертировать коды ошибок в строки

Функция error_category::message() преобразует значение ошибки в описывающую её строку:

Когда вы вызываете функцию error_code::message() , error_code , в свою очередь, вызывает указанную выше виртуальную функцию для получения сообщения об ошибке.

Важно помнить, что эти сообщения об ошибках должны быть автономными. Они могут быть записаны (в файл лога, скажем) в той точке программы, где дополнительный контекст не доступен. Если вы обертываете существующий API, который использует сообщения об ошибках с «вставками», вам придется создавать свои собственные сообщения. Например, если HTTP API использует строку сообщения «HTTP version %d.%d not supported» , эквивалентное автономное сообщение будет «HTTP version not supported» .

Модуль не предоставляет никакой помощи, когда дело доходит до локализации этих сообщений. Вполне вероятно, что сообщения, исходящие из категорий ошибок стандартной библиотеки, будут основаны на текущей локали. Если в вашей программе требуется локализация, я рекомендую использовать тот же подход.

(Немного истории: LWG осознавала необходимость локализации, но не было никакого дизайна, который удовлетворительно согласовал локализацию с расширяемостью. Вместо того, чтобы собирать комитеты для решения этого вопроса, LWG предпочла ничего не говорить о локализации сообщений об ошибках в стандарте.)

Шаг 5: уникальная идентификация категории

Идентификатор объекта, унаследованного от error_category , определяется его адресом. Это означает, что когда вы пишете:

Условие if оценивается так, как если бы вы написали:

Следуя примеру, установленному стандартной библиотекой, вы должны предоставить функцию для возврата ссылки на объект категории:

Эта функция всегда должна возвращать ссылку на один и тот же объект. Один из способов это сделать — определить глобальный объект в файле исходного кода и возвращать ссылку на него:

Тем не менее, глобальные переменные вызывают проблемы, связанные с порядком инициализации между модулями. Альтернативный подход заключается в использовании локальной статической переменной:

В этом случае объект категории инициализируется при первом использовании. C++0x также гарантирует, что инициализация потокобезопасна. (C++03 не давал такой гарантии).

История: На ранних этапах проектирования мы рассматривали использование целого числа или строки для идентификации категорий. Основная проблема с этим подходом заключалась в обеспечении уникальности в сочетании с расширяемостью. Если категория будет идентифицирована целым числом или строкой, что предотвратит коллизии между двумя несвязанными библиотеками? Using object identity leverages the linker in preventing different categories from having the same identity. Furthermore, storing a pointer to a base class allows us to make error_codes polymorphic while keeping them as copyable value types.

Шаг 6: построить error_code из enum

Как я показал в части 3, реализация требует функцию с названием make_error_code() , чтобы связать значение ошибки с категорией. Для ошибок HTTP эта функция могла бы выглядеть следующим образом:

Для полноты картины вы также должны предоставить эквивалентную функцию для построения error_condition :

Поскольку реализация находит эти функции используя ADL, вы должны поместить их в то же пространство имен, что и тип http_error .

Шаг 7: запись для неявного преобразования в error_code

Чтобы перечисляемые значения http_error могли использоваться как константы error_code , включите конструктор преобразования, используя шаблон is_error_code_enum :

Шаг 8 (опциональный): определить условия ошибок по умолчанию

Некоторые из описанных вами ошибок могут иметь аналогичные по смыслу условия ошибок из errc . Например, код состояния HTTP 403 Forbidden означает то же самое, что и std::errc::permission_denied .

Виртуальная функция error_category::default_error_condition() позволяет определить условие ошибки, эквивалентное данному коду ошибки. (Определение эквивалентности было описано во второй части.) Для ошибок HTTP вы можете написать:

Если вы решите не переопределять эту виртуальную функцию, то error_condition будет имеет такую же ошибку и категорию как и error_code . Это поведение по умолчанию, как в примере, показанном выше.

Использование кодов ошибок

Теперь вы можете использовать перечисляемые значения http_error как константы error_code , как при установке ошибки:

так и при ее проверке:

[Примечание переводчика: следует заметить, что при такой реализации не будет работать принцип о котором сказано выше — нулевое значение = успех — соответственно, приведение к bool тоже не будет работать]

Поскольку значения ошибок основаны на кодах состояния HTTP, мы также можем установить error_code непосредственно из ответа:

Кроме того, вы можете использовать этот метод при обертке ошибок, создаваемых уже существующей библиотекой.

Наконец, если вы определили отношение эквивалентности на шаге 8, вы можете написать:

без необходимости знать точный источник условия ошибки. Как поясняется в части 2, изначальный код ошибки (например, http_error::forbidden ) сохраняется, так что никакая информация не теряется.

В следующей части я покажу как создавать и использовать error_condition .

Часть 5

Создание собственных условий ошибок

Расширяемость модуля не ограничена кодами ошибок: error_condition так же можно расширить.

Зачем создавать свои условия ошибок?

Чтобы ответить на этот вопрос, давайте вернемся к различиям между error_code и error_condition :

  • сlass error_code — представляет собой конкретное значение ошибки, возвращаемое операцией (например, системным вызовом).
  • class error_condition — что-то, что вы хотите проверить и, возможно, среагировать на это в своем коде.

Это предлагает некоторые варианты использования для пользовательских условий ошибок:

    Абстракция платформозависимых ошибок.
    Предположим, вы пишете переносимую обертку вокруг getaddrinfo() . Два интересных условия ошибки: предварительная «имя не разрешается в данный момент, повторите попытку позже» и точная «имя не разрешено». Функция getaddrinfo() сообщает об этих ошибках следующим образом:
  • На платформах POSIX это ошибки EAI_AGAIN и EAI_NONAME , соответственно. Значения ошибок находятся в отдельном «пространстве имен» для значений errno. Это означает, что вам придется внедрить новую error_category для них.
  • В Windows это ошибки WSAEAI_AGAIN и WSAEAI_NONAME . Хотя имена похожи на ошибки POSIX, они разделяют «пространство имен» GetLastError() . Следовательно, вы можете повторно использовать std::system_category() для представления ошибок getaddrinfo() на этой платформе.

Чтобы избежать утраты информации, вы хотите сохранить изначальный код платформозависимой ошибки, одновременно предоставляя два условия ошибки (называемые, скажем, name_not_found_try_again и name_not_found ), которые могут быть проверены пользователями API.

  • Представление контекстозависимых значений для общих кодов ошибок.
    Большинство системных вызовов POSIX используют errno для сообщения об ошибках. Вместо того, чтобы определять новые коды ошибок для каждой функции, используются одни и те же коды, и вам может потребоваться просмотреть соответствующую страницу руководства, чтобы определить значение. Если вы реализуете свои собственные абстракции поверх этих системных вызовов, этот контекст теряется для пользователя.

    Скажем, вы хотите реализовать простую базу данных, где каждая запись хранится в отдельном файле. Когда вы пытаетесь прочитать запись, база данных вызывает open() для доступа к файлу. Эта функция устанавливает errno в ENOENT , если файл не существует.

    Поскольку механизм хранения базы данных абстрагируется от пользователя, было бы удивительно просить их проверять условие no_such_file_or_directory . Вместо этого вы можете создать собственное контекстозависимое условие ошибки no_such_entry , эквивалентное ENOENT .
    Проверка набора связанных ошибок.
    По мере роста вашей кодовой базы вы можете обнаружить, что один и тот же набор ошибок проверяется снова и снова. Возможно, вам нужно отреагировать на нехватку системных ресурсов:

    • not_enough_memory
    • resource_unavailable_try_again
    • too_many_files_open
    • too_many_files_open_in_system
    • .

    в нескольких местах, но последующее действие отличается в каждой точке использования. Это показывает, что существует более общее условие: «не хватает системных ресурсов», которое вы хотите проверить и отреагировать на него в своем коде.

    Пользовательское условие ошибки low_system_resources может быть определено таким образом, чтобы его эквивалентность основывалась на сочетании других условий ошибки. Это позволит писать вам проверки следующим образом:

    и таким образом исключить повторение отдельных проверок.

    Как вы увидите ниже, определение error_condition аналогично определению error_code .

    Шаг 1: определить значения ошибок

    Вам нужно создать enum для значений ошибок, аналогично std::errc :

    Фактические значения, которые вы используете, не важны, но вы должны убедиться, что они различны и отличны от нуля.

    Шаг 2: определить класс error_category

    Объект error_condition состоит из значения ошибки и категории. Чтобы создать новую категорию, нужно отнаследовать класс от error_category :

    Шаг 3: дать категории человеко-читаемое имя

    Виртуальная функция error_category::name() должна возвращать строку, идентифицирующую категорию:

    Шаг 4: конвертировать условия ошибок в строки

    Функция error_category::message() преобразует значение ошибки в описывающую её строку:

    Однако, в зависимости от вашего варианта использования, вызов error_condition::message() может быть маловероятным. В этом случае вы можете воспользоваться сокращением и просто написать:

    Шаг 5: реализовать эквивалентность ошибок

    Виртуальная функция error_category::equivalent() используется для определения эквивалентности кодов ошибок и условий. Есть две перегрузки этой функции. Первая:

    используется для установления эквивалентности между error_code в текущей категории и произвольными error_condition . Вторая перегрузка:

    определяет эквивалентность между error_condition в текущей категории и произвольными error_code . Поскольку вы создаете условия ошибки, вам нужно переопределить вторую перегрузку.

    Определение эквивалентности простое: верните true , если вы хотите, чтобы error_code был эквивалентен вашему условию, иначе верните false .

    Если вы намерены абстрагироваться от платформозависимых ошибок, вы можете реализовать error_category::equivalent() следующим образом:

    (Очевидно, что getaddrinfo_category() тоже нужно где-то определить.)

    Проверки могут комплексными, а так же могут повторно использовать другие константы error_condition :

    Шаг 6: уникальная идентификация категории

    Вы должны определить функцию для возврата ссылки на объект категории:

    которая всегда возвращает ссылку на один и тот же объект. Как и в случае с кодами ошибок, вы можете использовать глобальную переменную:

    или вы можете использовать статические потокобезопасные переменные из C++0x:

    Шаг 7: построить error_condition из enum

    Реализация требует функцию с названием make_error_code() , чтобы связать значение ошибки с категорией:

    Для полноты картины вам также необходимо определить эквивалентную функцию для построения error_code . Я оставлю это как упражнение для читателя.

    Шаг 8: запись для неявного преобразования в error_condition

    Наконец, чтобы перечисляемые значения api_error могли использоваться как константы error_condition , включите конструктор преобразования, используя шаблон is_error_condition_enum :

    Использование условий ошибок

    Теперь перечисляемые значения api_error могут использоваться как константы error_condition , так же как те, которые определены в std::errc :

    Как я уже несколько раз говорил, изначальный код ошибки сохраняется и информация не теряется. Не имеет значения, пришел ли этот код ошибки из операционной системы или из библиотеки HTTP со своей собственной категорией ошибок. Ваши пользовательские условия ошибки смогут одинаково хорошо работать в любом случае.

    В следующей, вероятно, последней, части я расскажу как создавать API, которые используют .

    Источник

  • Here is a list of the more common errors you might see generated by make, and some information about what they mean and how to fix them.

    Sometimes make errors are not fatal, especially in the presence of a - prefix on a recipe line, or the -k command line option. Errors that are fatal are prefixed with the string ***.

    Error messages are all either prefixed with the name of the program (usually ‘make’), or, if the error is found in a makefile, the name of the file and line number containing the problem.

    In the table below, these common prefixes are left off.

    [foo] Error NN
    [foo] signal description

    These errors are not really make errors at all. They mean that a program that make invoked as part of a recipe returned a non-0 error code (‘Error NN’), which make interprets as failure, or it exited in some other abnormal fashion (with a signal of some type). See Errors in Recipes.

    If no *** is attached to the message, then the sub-process failed but the rule in the makefile was prefixed with the - special character, so make ignored the error.

    missing separator. Stop.
    missing separator (did you mean TAB instead of 8 spaces?). Stop.

    This means that make could not understand much of anything about the makefile line it just read. GNU make looks for various separators (:, =, recipe prefix characters, etc.) to indicate what kind of line it’s parsing. This message means it couldn’t find a valid one.

    One of the most common reasons for this message is that you (or perhaps your oh-so-helpful editor, as is the case with many MS-Windows editors) have attempted to indent your recipe lines with spaces instead of a tab character. In this case, make will use the second form of the error above. Remember that every line in the recipe must begin with a tab character (unless you set .RECIPEPREFIX; see Special Variables). Eight spaces do not count. See Rule Syntax.

    recipe commences before first target. Stop.
    missing rule before recipe. Stop.

    This means the first thing in the makefile seems to be part of a recipe: it begins with a recipe prefix character and doesn’t appear to be a legal make directive (such as a variable assignment). Recipes must always be associated with a target.

    The second form is generated if the line has a semicolon as the first non-whitespace character; make interprets this to mean you left out the «target: prerequisite» section of a rule. See Rule Syntax.

    No rule to make target `xxx‘.
    No rule to make target `xxx‘, needed by `yyy‘.

    This means that make decided it needed to build a target, but then couldn’t find any instructions in the makefile on how to do that, either explicit or implicit (including in the default rules database).

    If you want that file to be built, you will need to add a rule to your makefile describing how that target can be built. Other possible sources of this problem are typos in the makefile (if that file name is wrong) or a corrupted source tree (if that file is not supposed to be built, but rather only a prerequisite).

    No targets specified and no makefile found. Stop.
    No targets. Stop.

    The former means that you didn’t provide any targets to be built on the command line, and make couldn’t find any makefiles to read in. The latter means that some makefile was found, but it didn’t contain any default goal and none was given on the command line. GNU make has nothing to do in these situations. See Arguments to Specify the Makefile.

    Makefile `xxx‘ was not found.
    Included makefile `xxx‘ was not found.

    A makefile specified on the command line (first form) or included (second form) was not found.

    warning: overriding recipe for target `xxx
    warning: ignoring old recipe for target `xxx

    GNU make allows only one recipe to be specified per target (except for double-colon rules). If you give a recipe for a target which already has been defined to have one, this warning is issued and the second recipe will overwrite the first. See Multiple Rules for One Target.

    Circular xxx <- yyy dependency dropped.

    This means that make detected a loop in the dependency graph: after tracing the prerequisite yyy of target xxx, and its prerequisites, etc., one of them depended on xxx again.

    Recursive variable `xxx‘ references itself (eventually). Stop.

    This means you’ve defined a normal (recursive) make variable xxx that, when it’s expanded, will refer to itself (xxx). This is not allowed; either use simply-expanded variables (‘:=’ or ‘::=’) or use the append operator (‘+=’). See How to Use Variables.

    Unterminated variable reference. Stop.

    This means you forgot to provide the proper closing parenthesis or brace in your variable or function reference.

    insufficient arguments to function `xxx‘. Stop.

    This means you haven’t provided the requisite number of arguments for this function. See the documentation of the function for a description of its arguments. See Functions for Transforming Text.

    missing target pattern. Stop.
    multiple target patterns. Stop.
    target pattern contains no `%’. Stop.
    mixed implicit and static pattern rules. Stop.

    These errors are generated for malformed static pattern rules (see Syntax of Static Pattern Rules). The first means the target-pattern part of the rule is empty; the second means there are multiple pattern characters (%) in the target-pattern part; the third means there are no pattern characters in the target-pattern part; and the fourth means that all three parts of the static pattern rule contain pattern characters (%)–the first part should not contain pattern characters.

    If you see these errors and you aren’t trying to create a static pattern rule, check the value of any variables in your target and prerequisite lists to be sure they do not contain colons.

    warning: -jN forced in submake: disabling jobserver mode.

    This warning and the next are generated if make detects error conditions related to parallel processing on systems where sub-makes can communicate (see Communicating Options to a Sub-make). This warning is generated if a recursive invocation of a make process is forced to have ‘-jN’ in its argument list (where N is greater than one). This could happen, for example, if you set the MAKE environment variable to ‘make -j2’. In this case, the sub-make doesn’t communicate with other make processes and will simply pretend it has two jobs of its own.

    warning: jobserver unavailable: using -j1. Add `+’ to parent make rule.

    In order for make processes to communicate, the parent will pass information to the child. Since this could result in problems if the child process isn’t actually a make, the parent will only do this if it thinks the child is a make. The parent uses the normal algorithms to determine this (see How the MAKE Variable Works). If the makefile is constructed such that the parent doesn’t know the child is a make process, then the child will receive only part of the information necessary. In this case, the child will generate this warning message and proceed with its build in a sequential manner.

    warning: ignoring prerequisites on suffix rule definition

    According to POSIX, a suffix rule cannot contain prerequisites. If a rule that could be a suffix rule has prerequisites it is interpreted as a simple explicit rule, with an odd target name. This requirement is obeyed when POSIX-conforming mode is enabled (the .POSIX target is defined). In versions of GNU make prior to 4.3, no warning was emitted and a suffix rule was created, however all prerequisites were ignored and were not part of the suffix rule. Starting with GNU make 4.3 the behavior is the same, and in addition this warning is generated. In a future version the POSIX-conforming behavior will be the only behavior: no rule with a prerequisite can be suffix rule and this warning will be removed.

    10 More Discussions You Might Find Interesting

    1. OS X (Apple)

    Alternatives to GNU Make for users of newer OS X

    As you may already know, Apple has integrated all the GNU developer tools into their own graphical development environment so you can no longer use them from the command line. This means that open source software that is distributed as source is inaccessible to users of newer versions of Mac OS X,… (4 Replies)

    Discussion started by: Ultrix

    2. Shell Programming and Scripting

    extension building make GNU Linux

    Hi,
    I do «touch poub.mlc»
    Download my Makefile,

    But when I try : make -f Makefile.txt(.txt in order my file to be accepted for upload), it says:»no rule for poub.mlo target»
    What is wrong in my Makefile(test yourself please before posting)? (1 Reply)

    Discussion started by: synhedionn

    3. Shell Programming and Scripting

    GNU make doesn’t pick up changes

    It’s been a while since I had to write a Makefile, but I’ve managed to clobber this together:
    SRC=module1.c module2.c
    OBJS=$(SRC:%.c=%.o)
    HDR=include1.h include2.h

    CC=gcc
    CFLAGS=-Wall -ggdb -D_XOPEN_SOURCE -I. -ansi

    all: program

    program: $(OBJS)
    $(CC) $(CFLAGS) -o $@ $(OBJS)
    (3 Replies)

    Discussion started by: pludi

    4. AIX

    Not picking up the GNU version of make

    Hi team,

    I am new to unix,and need your advice on the below.
    I am using aix5.3 and have installed make-3.82 on the server.
    I need to use gmake for configuring and installing a package ,but it is giving error at the below

    root@sapsrp:/usr/tmp/xymon-4.3.2
    # ./configure.server

    (7 Replies)

    Discussion started by: sonal kumar

    5. AIX

    gnu make fir AIX 5.3 and gcc 3.3.2

    Hi all
    i had installed gcc version 3.3.2 on my AIX 5.3 machine.
    which make utility i have to instal so it will fit the gcc version

    tnx
    skfn (2 Replies)

    Discussion started by: skfn1203

    6. Programming

    A question about Makefile run by GNU make

    Hello everybody,

    Currently I’m learning how to build projects (C programming) with GNU make. I have a problem with one Makefile and I would appreciate if you could kindly give me a hand. Here is the environment:

    OS: Redhat linux 5
    compiler: gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44)… (2 Replies)

    Discussion started by: dariyoosh

    7. SCO

    GNU cpp and make on SCO

    Installed GNU make and the GNU C/C++ compiler on SCO Openserver 5.0.7 recently.

    Only a normal user is able to run make and compile programs — root is not able to.

    Under root, make do run, but root can’t access the compiler at all, even tho I set root’s path to point to the compiler…. (0 Replies)

    Discussion started by: The_Librarian

    8. Programming

    Detecting host OS in Gnu make

    Is this possible? Right now I’m developing some code for Linux, Windows and OS X. To build on each of the systems I’ve currently got separate build targets for each platform. Ideally I’d like to set up a single make rule that can detect the appropriate target and build it.

    I know this is… (2 Replies)

    Discussion started by: G_Morgan

    10. UNIX for Advanced & Expert Users

    Problems with gnu make

    I am running a make file through the gnu make tool and i am getting the following error

    jsh1035c:/users/egate453/admegate/kapil/samples $ make -f GNUmakefile queue_c
    make -f ./GNUmakefile queue_c in_objdir=1 build_root=/users/egate453/admegate/kapil/samples
    make: Entering directory… (2 Replies)

    Discussion started by: handak9

    Содержание

    No rule to make target
    GNUmakefile:1: *** missing separator. Stop.
    Syntax error : end of file unexpected (expecting «fi»)
    OLDPWD not set
    @echo: command not found
    -bash: make: command not found
    Похожие статьи

    No rule to make target

    make: *** No rule to make target ‘main.cpp’, needed by ‘main.o’. Stop.

    GNUmakefile:1: *** missing separator. Stop.

    Если вы видите ошибку

    GNUmakefile:1: *** missing separator. Stop.

    Обратите внимание на GNUmakefile:1:

    1 — это номер строки, в которой произошла ошибка

    Возможно где-то вместо табуляции затесался пробел. Напоминаю, что в makefile отступы должны быть заданы табуляциями.

    Либо таргет перечислен без двоеточия .PHONY clean вместо .PHONY: clean

    Либо какая-то похожая ошибка.

    Syntax error : end of file unexpected (expecting «fi»)

    Если вы видите ошибку

    Syntax error : end of file unexpected (expecting «fi»)

    Обратите внимание на расстановку ; в конце выражений и расстановку при переносе строк.

    Изучите этот

    пример

    и сравните со своим кодом.

    OLDPWD not set

    Если внутри makefile вы выполняете cd и видите ошибку

    OLDPWD not set

    Попробуйте сперва явно перейти в текущую директорию с помощью

    CURDIR

    cd $(CURDIR)

    @echo: command not found

    Если внутри makefile вы пытаетесь подавить вывод echo и получаете

    @echo: command not found

    Скорее всего echo это не первая команда в строке

    НЕПРАВИЛЬНО:

    if [ ! -f /home/andrei/Downloads/iso/centos_netinstall.iso ]; then
    rm ./CentOS-7-x86_64-NetInstall-*;
    wget -r -np «http://builder.hel.fi.ssh.com/privx-builds/latest/PrivX-master/Deliverables/» -A «CentOS-7-x86_64-NetInstall-2009.iso

    -*.iso;
    else
    @echo «WARNING: centos_netinstall.iso already exists»;

    ПРАВИЛЬНО:

    @if [ ! -f /home/andrei/Downloads/iso/centos_netinstall.iso ]; then
    rm ./CentOS-7-x86_64-NetInstall-*;
    wget -r -np «http://builder.hel.fi.ssh.com/privx-builds/latest/PrivX-master/Deliverables/» -A «CentOS-7-x86_64-NetInstall-2009.iso

    -*.iso;
    else
    echo «WARNING: centos_netinstall.iso already exists»;

    -bash: make: command not found

    Ошибка

    -bash: make: command not found

    Означает, что make не установлен.

    Установить make в rpm системах можно с помощью yum в deb система — с помощью apt

    sudo yum -y install make

    sudo apt -y install make

    Похожие статьи

    make
    Основы make
    PHONY
    CURDIR
    shell
    wget + make
    Переменные в Make файлах
    ifeq: Условные операторы
    filter
    -c: Компиляция
    Linux
    Bash
    C
    C++
    C++ Header файлы
    Configure make install
    DevOps
    Docker
    OpenBSD
    Errors make

    Понравилась статья? Поделить с друзьями:
  • Make error code 127
  • Main cs 2 0 error cs1024 wrong preprocessor directive
  • Main cpp 3 10 error qapplication file not found
  • Main cpp 2 10 error qqmlapplicationengine file not found
  • Make error 255 linux