יש לכם blacklist של דברים שלא נכנסים לsource control?

יבגני34

New member
ואם במקרה הקומפיילר שונה?

אז תכניסו גם את ה-toolchain ל- source control,
נראה לי שאתם לא מבינים מה שאתם עושים
 

הפרבולה

New member
הקומפילר לא שונה, זה שטות להשתמש בקומפילרים שונים

כל פעם בעיקר עבור תוכנת firmware . זה מכניס אי וודאות גדולה , למשל אם פתאום התוכנה לא מתנהגת "כתמול שילשום" , עכשיו לך תדע אם זה בגלל שהיא קומפלה בקומפילר שונה או בגלל שזה בגלל קלט שונה \בג וכו.
&nbsp
אם כבר משדרגים קומפילר אז דואגים שכולם יעברו אליו , ומעתה והלאה משמשים רק בו . ( את הקומפילר הישן משאירם רק כדי לתמוך במעבדים ישנים למרות שיתכן שהחדש תומך גם במעבדים הישנים )
 

BravoMan

Active member
אני באמת לא יודע איך דברים עובדים

בעולם ה-DSP, או מיקרו בקרים, אבל אני לא מכיר קומפיילר רציני מחוץ לתחומים האלה שלא מכניס חתימה ותאריך לבינאריים בכל קימפול.
&nbsp
בהנחה שעושים השוואה דרך כלי hash רגיל (מה ש-cvs בד"כ יעשה) ולא דרך משהו מורכב וייעודי שממש מנתח את הבינארי ברמה של sections (ואני עוד מדבר כאן על תוצארים מקומפלים native שבכלל לא רלוונטיים לשואל המקורי) אין שום סיכוי שבעולם שיצא לך בינארי זהה משתי בניות נפרדות גם אם לא שינית בכלל את קבצי המקור.
&nbsp
אגב, השואל עובד ב-Java, שם שמות מחלקות, איברי מחלקה ופונקציות נשארים בצורתם המקורית גם בתוך הבינארי המקומפל, מה שאומר ששינוי בשם של כל דבר שאינו משתנה מקומי או פרמטר יגרור בכל מקרה שינוי משמעותי בבינארי.
&nbsp
יש רק דרך אחת רצינית ונכונה לוודא שהקוד ממשיך לעבוד כפי שעבד בעבר לאחר שינויים בקבצי המקור - להריץ סט בדיקות מקיף.
&nbsp
לגבי עבודה באותה סביבת פיתוח - אני יודע שבעולם MS זה נושא כואב, אבל בכל מקום מחוץ לחור השחור שנקרא redmond סביבת פיתוח היא לא מקשה אחת קשיחה.
&nbsp
קומפיילר הוא רכיב נפרד, מערכת ה-build היא רכיב נפרד, והממשק הגרפי לכל העסק הוא רכיב נפרד.
&nbsp
למשל, שתי סביבות הפיתוח שהשואל המקורי הזכיר - Eclipse ו-IntelliJ עובדות מתחת למכסה המנוע עם אותו קומפיילר בדיוק, ובכל מקרה שתיהן כתובות בעצמן ב-Java (לא שזה כ"כ רלוונטי).
&nbsp
שתיהן גם יכולות להשתמש באותה מערכת בניה (אם כי זו לא בררת מחדל) מה שאומר שקבצי הקונפיגורציה הרלוונטיים - שהם למעשה קבצי בניה, יהיו זהים.
 

הפרבולה

New member
הקומפילר שאני עובד איתו לתוכנת מיקרו בקר מיצר בדיוק אותו קוד

בינרי כל קימפול במידה ולא השתנו קבצי המקור מהותית , כלומר שינוי של הערות או שמות משתנים או העברת קוד למקרואים עדין לא גורם לכך שיווצר קוד בינרי שונה אחרי קימפול.
יש כמובן טסטים לתוכנה אבל בהנחה ש firmware עבר את הטסטים , אז בהנתן ששינינו רק הערות\מקרואים\שמות וכו (כדי לשפר את קריאות הקוד) אז מיותר לגמרי להריץ שוב את אותם טסטים , מספיק לבדוק שהקוד הבינרי לא השתנה.

חבל שאי אפשר לעשות זאת עם הקומפילר של VS שמשנה כל פעם את הקוד גם אם שיניתי רק הערות .
 

BravoMan

Active member
לא בדיוק:

אני מבין שכשעובדים עם בקר, יצור מאוד מצומם, כל בית בזיכרון חשוב ולכן אין מקום לדברים כמו חתימות.
&nbsp
אבל כשעובדים עם דברים שמכילים מעבד אמתי, אפילו אם זה לא מחשב או טלפון חכם אלא סתם קופסה כמו נתב או ממיר כבלים, הסיפור הוא שונה לגמרי.
&nbsp
לא רק שחשוב להטמיעה מספר בניה (בד"כ נעשה אוטומטית), מספר גרסה וחותמת זמן בבינארי, לרוב חותמים את קובץ ה-firmware (או קובץ הריצה) עם תעודה קריפטוגרפית כדי שניתן יהיה לוודא שלא נעשה בו שינוי, או אפילו לוודא שעדכון, אם קיים מגיע מאותו מקום כמו ה-firmware המקורי.
&nbsp
(יש לציין ש-firmware ללחות עם SoC שונה מהותית מ-firmware של בקר ולרוב כולל למעשה התקנה של מערכת הפעלה, כי אלה רק במקרים נדירים עובדים bare metal).
&nbsp
בקיצור - מחוץ לעולם הבקרים הרבה פעמים יותר חשובה היכולת לזהות את הבינארי מאשר היכולת לקבל את אותו ה-hash משני build שונים.
&nbsp
אגב, גם לסטודיו יש יכולת חתימה (על EXE ו-DLL).
למשל, חתימה על דרייברים ל-Windows 64 bit היא חובה, אחרת המערכת לא תטען אותם, וחברות רציניות גם משקיעות בחתימות על סתם EXE להתקנת תוכנות כדי שכשהמערכת תשאל את המשתמש "האם אתה בטוח שאתה סומך על X לעשות שינויים במחשב שלך?" יופיע שם של החברה בצורה תקינה.
&nbsp
במערכות ניידות, עניין סימוני גרסאות והחתימות חשוב עוד יותר ובקלות יכול להפיל התקנה של הקובץ, אם המערכת חושדת שמשהו לא תקין איתו.
 

הפרבולה

New member
הקומפילר של המיקרו מעבד שאנחנו עובדים איתו מייצר תמיד אותו

קוד עבור אותם קבצי מקור, אח"כ יש לנו כלי שמוסיף לתחילת הקובץ חתימה של תאריך ועוד דברים .
שאני משווה 2 קבצים בינריים כאלו אני מתעלם מהשוני בחתימה ואם הכל זהה חוץ מהחתימה אז המבחן עבר בהצלחה ( בהנחה שצפיתי שהשינוים שביצעתי לא אמורים היו לשנות את הקוד ). אם יש בכל זאת שינוי אז כנראה ששיניתי קוד שלא הייתי אמור לשנות או שקימפלתי קבצי מקור לא נכונים.
אם באמת שיניתי את הקוד אז אני לא מצפה לקבל קובץ בינרי זהה לקודם , וצריך להריץ בדיקות אחרות ארוכות יותר בהתאם לשינוי שבוצע.
 

הפרבולה

New member
אגב יכול להיות שגם לגבי EXE ו DLL שמיצר ה VS ניתן להשוות

קבצים בינריים לקבצים בינריים קודמים בהנחה שיודעים להתעלם מהשוני מהחתימה שלהם ומשווים רק את הקוד "האמיתי"
 

BravoMan

Active member
מבחינה טכנית זה אפשרי.

שני סוגי הקבצים בנויים למעשה באותו פורמט שמתועד די טוב, ומחולקים למקטעים (sections).
אפשר לכתוב כלי שמבודד את ה-section של הקוד (.text) ומריץ hash או ממש השוואה בית בית רק עליו.
&nbsp
אישית, אני לא מכיר כלים כאלה, ואני חושב שהסיבה לכך היא שבתחומי פיתוח בהם לי אישית יצא לעבוד, ה-use cases שהצגת מאוד נדירים.
&nbsp
למשל, למה לשנות רק הערה?
המקרה היחיד שאני יכול לחשוב עליו בו יהיה שינוי בהערה שאינו מלווה שינוי בקוד זה אם מישהו עובר ומתקן עבודה של מתכנת אחר רשלן ששינה את הקוד בלי לעדכן הערה רלוונטית.
&nbsp
מקרה של מקרואים לא רלוונטי להרבה שפות (לא יודע מי חוץ ממשפחת C המידית מיישם אותם), ושינוי שמות משתנים (במיוחד מקומיים), בד"כ יבוצע כחלק מעבודות אחרות שכן ישנו קוד.
&nbsp
אבל שוב - אני מדבר כאן על פיתוח ב-userspace של מובייל, Desktop ושרתים.
(במקרה של שרתים הרבה פעמים בכלל אין בינארי סופי, אז כל הדיון לא רלוונטי כי השפות בשימוש הן JS, PHP, Python, Ruby וכו').
 

הפרבולה

New member
למה לשנות רק הערה?

לפעמים הערה שכתובה לא טובה\לא נכונה \ לא ממצה אולי מיותרת ומבלבלת ... יכול להיות הרבה סיבות ולא משנה מי כתב אותה.
לדוגמה לפעמים אני כותב קוד בלי הערות בכלל מריץ ובודק, משנה מריץ בודק .. בסיום הפיתוח של אותו קוד ( אחרי שהבנתי באמת מה עשיתי
)אני ניגש להוסיף הערות כדי שבעתיד אני ואחרים יבינו .
כעת שכבר הקוד גמור ובדוק לא הייתי רוצה שתוך כדי הוספת הערות אני אשנה אקראית בטעות את הקוד עצמו ולכן אני מקמפל שוב את הקוד עם ההערות ובודק שהתוצר הבינרי לא השתנה .
 

choo

Active member
מסתכלים ב-diff

&nbsp
באופן כללי - סכל פעם שבצעים שינויים בקוד ולפני שעושים commit - עוברים על כל השינויים הרלונטיים ומוודאים לא נשאר בטעות משהו שלא אמור להיכנס.
&nbsp
זה משהו שרצוי לעשות לפני כל commit - במיוחד כשאתה משנה את הקוד. תיקון הערות לא שונה מהותית מתיקון קוד, וצריך להתייחס אליו באותה צורה.
 

הפרבולה

New member
הרבה יותר מהר ובטוח לוודא שהקובץ הבינרי לא השתנה

לעומת הקובץ הקודם שנוצר מקומפילציה על קוד המקור לפני השינוי בההערות.

אגב יש עוד סוגי שינויים שלא אמורים לשנות את הקוד הבינרי, למשל אם קבוע מסוים מופיע בהרבה מקומות בקוד מתבקש להעביר אותו ל #define ושינוי כזה לא אמור לשנות את הקוד , ויכולות להיות בעיתיות של נתינת ערך לא באותו פורמט במקומות שונים שהקומפילר יפרש אחרת ממה שהתכוונתה ( למשל בחלק מהמקומות מופיע 7 ובחלק 7.0 ). השוואת הקוד הבינרי יכולה להעלות על דברים כאלו
 

choo

Active member
שוב - מדובר בפתרון לתת-תחום קטן של שינויי קוד

&nbsp
מה אתה עושה כשאתה כן משנה את הקוד?
 

הפרבולה

New member
שאני משנה קוד אז אין ברירה אלה להריץ טסטים מלאים

וגם אולי לבנות טסטים חדשים שבודקים את הפיצר החדש .
 

ipv6

Member
שינוי של define עלול לשנות לך את הבינארי

למשל אם משתמשים בערך שלו בשביל לאתחל משתנה גלובלי. הערך של הגלובאלי מאותחיל נשמר בתוך הבינארי ולכן שינוי של ה-define יכול לשנות לך את הבינארי.

#include <iostream>
#define STRING_OF_THE_DAY "WE_ALL_LOVE_ZASKE!!!!!!!"
char g_stringOfTheDay[] = STRING_OF_THE_DAY;

void main()
{}

מה שהגיוני ונכון לעשות זה בדיוק מה שchoo ציין. לפני כל push (או השפעה על repository מרכזי שמשפיע על עוד אנשים) להשתמש ב-Source Control ולוודא שמה שאתה דוחף זה בדיוק מה שהתכוונת לדחוף.
 

הפרבולה

New member
לא הבנתי את הנקודה שלך

ברור ששינוי ה #define עלול להשפיע על בינרי , אני דיברתי על להעביר קבוע ל #define שזה לא אמור לשנות את הבינרי ולכן חשוב להשוות כדי לודא שאכן זה לא שינה ( ואם כן אז יש בעיה), אני לא רואה איך השוואות קוד מקור יכולים לוודא זאת.
דוגמה לפני השינוי:

Cricle1 = 2*R1* 3.1415926535897932384626433832795
Cricle2 = 2*R2* 3.1415926535897932384626433832795
Cricle3 = 2*R3* 3.1415926535897932384626433832795

ואחרי השינוי

#define PI 3.1415926535897932384626433832795
Cricle1 = 2*R1* PI
Cricle2 = 2*R2* PI
Cricle3 = 2*R3* PI

כעת השוואת הקובץ הבינרי לקודם זה הכי בטוח לודא שאכן הקוד הבינרי לא השתנה כפי שאנו מצפים.
 

ipv6

Member
הנקודה מאד פשוטה

סט המקרים בהם השוואה של הבינאריים יעילה ולא תקבל false postive הוא נורא קטן וברב המוחלט של המקרים עדיין תצטרך להתעסק עם קוד מקור.[1]

נכון הבדיקה שנתת היא הכי חזקה שיש א-ב-ל : בשינויים בסגנון שאתה הראת, בדיקות סבירות מינוס מינוס ומעבר בעניים עם meld/tkdiff יתן תוצאה שאני מספיק בטוח בה כדי להכניס את השינויים.
דברים כמו בדוגמא שלך, כלומר הכנסה ל-define בלבד, ללא שינויי של שום דבר שמשפיע על הבינארי, זה כמות מזערית עד לא קיימת מהמשימות שאני עושה אז גם ככה זה לא רלוונטי.
בן אדם ששינויים כאלה זאת רב העבודה שלו..שיחליף עבודה.


1. צריך לוודא שהקומפיילר לא דוחף מידע שתלוי בתאריך בשעה ובעוד דברים לתוך הבינארי.
 

ipv6

Member
אפילו בדוגמא שלך

אם תקבע את הקבוע לא עם define אלא
static const double PI = 3.14;
(בהנחה שאתה כותב ב-C++) ישתנה לך הבינארי ותצטרך לעבוד עם העניים..
 

הפרבולה

New member
נכון ולכן אני אשתמש ב define ולא ב static const

כדי שהקוד הבינרי לא ישתנה ( ואוודא זאת על ידי השוואה לקודם ) , כי אם הבינרי ישתנה אני אאלץ להריץ הרבה טסטים כדי לוודא שהשינוי לא גרם לבג, יתכן אפילו שמדובר בקוד שרץ כבר שנים אצל לקוחות, ולך תחכה עכשיו עוד כמה שנים כדי להיות בטוח שלא גרמתה נזק בשינוי הזה.
 

הפרבולה

New member
זה לא רוב העבודה שלי

רוב העבודה שלי זה לכתוב קוד, אבל מדי פעם יש צורך "ליפות" קוד שעובד ונבדק ואז יש סיכון שעלולים בהיסח דעת לשנות אותו מבלי שהתכוונו .

אגב השוואת בינרים עזרה לנו גם לגלות בגים למשל הקוד הבא:

int a= 10;
float b = a/20;


לא הבנו למה מקבלים b=0 במקום b=0.5 כפי שציפינו , אחרי ששינינו את הקוד לזה:
int a= 10;
float b = a/20.0;

וראינו שהקוד הבינרי לא יוצא כמו מקודם הבנו שהקוד המקומפל מחשב אחרת את b בשני המקרים .
 

BravoMan

Active member
הייתם צריכים לראות מה הקומפיילר עושה

בשביל לקלוט באג שנובע מכלל בסיסי של עובדה עם נקודה צפה שמלמדים בכל קורס מבוא ל-C?

לא רציתי לכתוב זאת קודם, אבל היות והשרשור הזה ממשיך, אציין זאת:
אני מריח אצלכם הרגלי עבודה רעים, והם אלה שמביאים למצבים מוזרים.

למשל, הצורך בעריכה מסיבית של הערות בנפרד לעריכת הקוד אליהן הן שייכות מראה על הרגלי תיעוד רעים.
אם לא שומרים על הערה תואמת לקוד בזמן אמת, היא בקלות הופכת לא רלוונטית, ואז לא רק שמבוזבז בסוף יותר זמן לתקן אותה כי צריך להבין מחדש מה קוד עושה, אלא גם עלולים להבין לא נכון וליצור הערה שנראית טוב אבל מטעה ומפריע בדיבג.

בנוסף, אם משתמשים בהערות בלוק סטייל C, לא כ"כ קל בטעות לפגוע בקוד. זה חשש מעט מוזר.
הרבה יותר מסוכן השימוש ב-define, בגלל אופי ה-copy / paste שלו.

למשל, הקוד הזה יתקמפל תקין, אבל לא ייתן את התוצאה הרצויה בגלל תו אחד קטן:
קוד:
#include <stdio.h>
  2 
  3 #define BUG 10;
  4 
  5 int main(int argc, char **argv) {
  6     int a = BUG;
  7     int i;
  8 
  9     for (i = 0; i < 10; i++) a = BUG + i + 1;
 10 
 11     printf("a = %d\n", a);
 12 
 13     return 0;
 14 }
האם אתה מזהה את התו?

כמו כן, define בקלות יכול להחביא בעיה מהסוג שהצגת בהודעתך שנובעת מכללי המרה אוטומטיים של C.

מה שמביא אותי לעוד נקודה:
אם כבר הולכים להכניס define הרי שלעשות את זה אחרי שהנתון המוחלף כבר נמצא בשימוש רחב הוא מתכון לטעויות.

טעויות שלא בהכרח יחשפו ע"י השוואת בינארים.
אני אתן לך דוגמה:
קוד:
/*** Code using magic numbers, no defines ***/

int num_of_fireman = 7;
int count = 0;

while (count < 7) {
  fireman_get_on_firetruck(count);
  count++;
}

/*** Lets use define to make the code more readable ***/
#define NUM_OF_FIREMAN_ON_DUTY    7

int num_of_fireman = NUM_OF_FIREMAN_ON_DUTY;
int count = 0;

while (count < NUM_OF_FIREMAN_ON_DUTY) {
  fireman_get_on_frietruck(count);
  count++;
}

/*** but what the code REALLY MEANS ***/
#define NUM_OF_FIREMAN_ON_DUTY    7
#define FIRE_TRUCK_CREW_SIZE      7

int num_of_fireman = NUM_OF_FIREMAN_ON_DUTY;
int count = 0;

while (count < FIRE_TRUCK_CREW_SIZE) {
  fireman_get_on_frietruck(count);
  count++;
}

עכשיו, הבינארי יצא זהה עם ובלי ה-define, אבל כשמחר יחליטו לשנות את מספר הכבאים בתחנה, בטעות ובלי לשים לב ישנו גם את מספר הכבאים שנכנסים בתוך משאית כיבוי, מה שלא נכון לחלוטין!

זו דוגמה מאוד מופשטת, אבל שוב - כשיש לך אותו נתון בהרבה מקומות בקוד ואתה מחליף אותו בדיעבד ב-define או קבוע, צריך להיות מאוד מאוד זהיר לוודא שאתה באמת מחליף את אותו הנתון בדיוק ולא נתון שבמקרה כרגע זהה.
שום השוואה בינארית לא תציל אותך מזה, לכן עדיף לקבוע קבועים מראש, או לפחות מוקדם ככל האפשר בתהליך כתיבת הקוד כשרואים שהנתון חוזר על עצמו.

אצלנו אגב, יש כלל זהב - בלי magic numbers בכלל!
בכל אופן, אין לי בעיה עם זה שאתם משווים בינארים או שומרים אותם ב-CSV, אבל ככל שהדיון נמשך אני רואה יותר ויותר למה זה לא מנהג נפוץ בתעשיה, ושאולי הבעיה היא פחות טבע התכנות למיקרו בקרים ויותר הרגלי ונהלי תכנות שכדאי לשקול לשנות ולתרגל כדי לחסוך זמן ובאגים פוטנציאליים.
 
למעלה